From e69de844cbf3243793517ebc510c2b27a4fa03fb Mon Sep 17 00:00:00 2001 From: englishman Date: Fri, 16 Sep 2016 18:21:25 -0400 Subject: [PATCH 01/15] test_is_successful_if_expected_exit_is_called_in_a_thread test improvement --- .../lang/system/ExpectedSystemExitTest.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/test/java/org/junit/contrib/java/lang/system/ExpectedSystemExitTest.java b/src/test/java/org/junit/contrib/java/lang/system/ExpectedSystemExitTest.java index ae1a40f5..f9ce52fc 100644 --- a/src/test/java/org/junit/contrib/java/lang/system/ExpectedSystemExitTest.java +++ b/src/test/java/org/junit/contrib/java/lang/system/ExpectedSystemExitTest.java @@ -1,21 +1,19 @@ package org.junit.contrib.java.lang.system; +import org.junit.Test; +import org.junit.runners.model.Statement; + +import java.security.Permission; + import static java.lang.System.getSecurityManager; import static java.lang.System.setSecurityManager; import static java.lang.Thread.sleep; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import static org.junit.contrib.java.lang.system.Executor.exceptionThrownWhenTestIsExecutedWithRule; import static org.junit.contrib.java.lang.system.Executor.executeTestWithRule; import static org.junit.contrib.java.lang.system.Statements.TEST_THAT_DOES_NOTHING; -import java.security.Permission; - -import org.junit.Test; -import org.junit.runners.model.Statement; - public class ExpectedSystemExitTest { private static final Object ARBITRARY_CONTEXT = new Object(); @@ -128,7 +126,7 @@ public void current_security_manager_is_used_for_anything_else_than_system_exit( @Test public void test_is_successful_if_expected_exit_is_called_in_a_thread() { rule.expectSystemExitWithStatus(ARBITRARY_EXIT_STATUS); - executeTestWithRule(new SystemExitInThread(), rule); + executeTestWithRule(new SystemExitInSeparateThread(), rule); } private static class SystemExit0 extends Statement { @@ -145,17 +143,21 @@ public void evaluate() throws Throwable { } } - private static class SystemExitInThread extends Statement { + private static class SystemExitInSeparateThread extends Statement { @Override public void evaluate() throws Throwable { - Runnable callSystemExit = new Runnable() { - public void run() { + new Thread(new LongExecutionBeforeExitCall()).start(); + } + + private static class LongExecutionBeforeExitCall implements Runnable { + public void run() { + try { + sleep(1000); System.exit(ARBITRARY_EXIT_STATUS); + } catch (InterruptedException e) { + e.printStackTrace(); } - }; - Thread thread = new Thread(callSystemExit); - thread.start(); - sleep(1000); // wait until the thread exits + } } } From deebe0da3078f399651a68a2e9c5ae454f69312e Mon Sep 17 00:00:00 2001 From: englishman Date: Fri, 16 Sep 2016 18:39:10 -0400 Subject: [PATCH 02/15] Add synch latch to NoExitSecurityManager --- .../java/lang/system/ExpectedSystemExit.java | 26 +++++++++++-------- .../internal/NoExitSecurityManager.java | 11 +++++--- .../internal/NoExitSecurityManagerTest.java | 22 +++++++--------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java index f798940c..1c503300 100644 --- a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java +++ b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java @@ -1,18 +1,18 @@ package org.junit.contrib.java.lang.system; -import static java.lang.System.getSecurityManager; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import java.util.ArrayList; -import java.util.Collection; - import org.junit.contrib.java.lang.system.internal.CheckExitCalled; import org.junit.contrib.java.lang.system.internal.NoExitSecurityManager; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; +import java.util.ArrayList; +import java.util.Collection; + +import static java.lang.System.getSecurityManager; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + /** * The {@code ExpectedSystemExit} allows in-test specification of expected * {@code System.exit(...)} calls. @@ -138,10 +138,14 @@ public void evaluate() throws Throwable { private void checkSystemExit() { NoExitSecurityManager securityManager = (NoExitSecurityManager) getSecurityManager(); - if (securityManager.isCheckExitCalled()) - handleSystemExitWithStatus(securityManager.getStatusOfFirstCheckExitCall()); - else - handleMissingSystemExit(); + try { + if (securityManager.isCheckExitCalled()) + handleSystemExitWithStatus(securityManager.getStatusOfFirstCheckExitCall()); + else + handleMissingSystemExit(); + } catch (InterruptedException e) { + e.printStackTrace(); + } } private void handleMissingSystemExit() { diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java index df7fee45..f1880601 100644 --- a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java +++ b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java @@ -3,6 +3,7 @@ import java.io.FileDescriptor; import java.net.InetAddress; import java.security.Permission; +import java.util.concurrent.CountDownLatch; /** * A {@code NoExitSecurityManager} throws a {@link CheckExitCalled} exception @@ -11,6 +12,7 @@ */ public class NoExitSecurityManager extends SecurityManager { private final SecurityManager originalSecurityManager; + private final CountDownLatch synchLatch = new CountDownLatch(1); private Integer statusOfFirstExitCall = null; public NoExitSecurityManager(SecurityManager originalSecurityManager) { @@ -21,19 +23,20 @@ public NoExitSecurityManager(SecurityManager originalSecurityManager) { public void checkExit(int status) { if (statusOfFirstExitCall == null) statusOfFirstExitCall = status; + synchLatch.countDown(); throw new CheckExitCalled(status); } - public boolean isCheckExitCalled() { + public boolean isCheckExitCalled() throws InterruptedException { + synchLatch.await(); return statusOfFirstExitCall != null; } - public int getStatusOfFirstCheckExitCall() { + public int getStatusOfFirstCheckExitCall() throws InterruptedException { if (isCheckExitCalled()) return statusOfFirstExitCall; else - throw new IllegalStateException( - "checkExit(int) has not been called."); + throw new IllegalStateException("checkExit(int) has not been called."); } @Override diff --git a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java index 86f2e74c..86b047e8 100644 --- a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java +++ b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java @@ -1,19 +1,17 @@ package org.junit.contrib.java.lang.system.internal; -import static com.github.stefanbirkner.fishbowl.Fishbowl.exceptionThrownBy; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import com.github.stefanbirkner.fishbowl.Statement; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; import java.io.FileDescriptor; import java.net.InetAddress; import java.security.Permission; -import com.github.stefanbirkner.fishbowl.Statement; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import static com.github.stefanbirkner.fishbowl.Fishbowl.exceptionThrownBy; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; public class NoExitSecurityManagerTest { private static final int DUMMY_STATUS = 1; @@ -440,18 +438,18 @@ public void getThreadGroup_may_be_called_without_original_security_manager() { } @Test - public void information_about_a_missing_checkExit_call_is_available() { + public void information_about_a_missing_checkExit_call_is_available() throws InterruptedException { assertThat(managerWithOriginal.isCheckExitCalled()).isFalse(); } @Test - public void information_about_a_checkExit_call_is_available() { + public void information_about_a_checkExit_call_is_available() throws InterruptedException { safeCallCheckExitWithStatus(DUMMY_STATUS); assertThat(managerWithOriginal.isCheckExitCalled()).isTrue(); } @Test - public void status_of_first_call_of_checkExit_is_available() { + public void status_of_first_call_of_checkExit_is_available() throws InterruptedException { safeCallCheckExitWithStatus(DUMMY_STATUS); safeCallCheckExitWithStatus(DUMMY_STATUS + 1); assertThat(managerWithOriginal.getStatusOfFirstCheckExitCall()) From 52c279546040169d7bab6bc56d79c4bfe2900d76 Mon Sep 17 00:00:00 2001 From: englishman Date: Wed, 28 Sep 2016 18:40:53 -0400 Subject: [PATCH 03/15] Add timeout for multi-threaded execution --- .../contrib/java/lang/system/ExpectedSystemExit.java | 10 +++++++--- .../lang/system/internal/NoExitSecurityManager.java | 12 +++++++++++- .../java/lang/system/ExpectedSystemExitTest.java | 3 ++- .../system/internal/NoExitSecurityManagerTest.java | 3 +-- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java index 1c503300..57ad1fa0 100644 --- a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java +++ b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java @@ -93,6 +93,7 @@ public static ExpectedSystemExit none() { private final Collection assertions = new ArrayList(); private boolean expectExit = false; private Integer expectedStatus = null; + private long timeout = 0; private ExpectedSystemExit() { } @@ -106,6 +107,10 @@ public void expectSystemExit() { expectExit = true; } + public void timeout(long timeout) { + this.timeout = timeout; + } + public void checkAssertionAfterwards(Assertion assertion) { assertions.add(assertion); } @@ -117,8 +122,7 @@ public Statement apply(final Statement base, Description description) { } private ProvideSecurityManager createNoExitSecurityManagerRule() { - NoExitSecurityManager noExitSecurityManager = new NoExitSecurityManager( - getSecurityManager()); + SecurityManager noExitSecurityManager = new NoExitSecurityManager(getSecurityManager(), timeout); return new ProvideSecurityManager(noExitSecurityManager); } @@ -144,7 +148,7 @@ private void checkSystemExit() { else handleMissingSystemExit(); } catch (InterruptedException e) { - e.printStackTrace(); + fail("Unexpected waiting loop break."); } } diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java index f1880601..2797ee2b 100644 --- a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java +++ b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java @@ -4,6 +4,7 @@ import java.net.InetAddress; import java.security.Permission; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * A {@code NoExitSecurityManager} throws a {@link CheckExitCalled} exception @@ -11,12 +12,20 @@ * delegated to the original security manager. */ public class NoExitSecurityManager extends SecurityManager { + private final SecurityManager originalSecurityManager; + private final long timeout; + private final CountDownLatch synchLatch = new CountDownLatch(1); private Integer statusOfFirstExitCall = null; public NoExitSecurityManager(SecurityManager originalSecurityManager) { + this(originalSecurityManager, 0); + } + + public NoExitSecurityManager(SecurityManager originalSecurityManager, long timeout) { this.originalSecurityManager = originalSecurityManager; + this.timeout = timeout; } @Override @@ -28,7 +37,8 @@ public void checkExit(int status) { } public boolean isCheckExitCalled() throws InterruptedException { - synchLatch.await(); + if (timeout > 0) + synchLatch.await(timeout, TimeUnit.MILLISECONDS); return statusOfFirstExitCall != null; } diff --git a/src/test/java/org/junit/contrib/java/lang/system/ExpectedSystemExitTest.java b/src/test/java/org/junit/contrib/java/lang/system/ExpectedSystemExitTest.java index f9ce52fc..fc86fafc 100644 --- a/src/test/java/org/junit/contrib/java/lang/system/ExpectedSystemExitTest.java +++ b/src/test/java/org/junit/contrib/java/lang/system/ExpectedSystemExitTest.java @@ -126,6 +126,7 @@ public void current_security_manager_is_used_for_anything_else_than_system_exit( @Test public void test_is_successful_if_expected_exit_is_called_in_a_thread() { rule.expectSystemExitWithStatus(ARBITRARY_EXIT_STATUS); + rule.timeout(1000); executeTestWithRule(new SystemExitInSeparateThread(), rule); } @@ -152,7 +153,7 @@ public void evaluate() throws Throwable { private static class LongExecutionBeforeExitCall implements Runnable { public void run() { try { - sleep(1000); + sleep(500); System.exit(ARBITRARY_EXIT_STATUS); } catch (InterruptedException e) { e.printStackTrace(); diff --git a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java index 86b047e8..81e0c5b2 100644 --- a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java +++ b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java @@ -20,8 +20,7 @@ public class NoExitSecurityManagerTest { public final TemporaryFolder temporaryFolder = new TemporaryFolder(); private final SecurityManager originalSecurityManager = mock(SecurityManager.class); - private final NoExitSecurityManager managerWithOriginal = new NoExitSecurityManager( - originalSecurityManager); + private final NoExitSecurityManager managerWithOriginal = new NoExitSecurityManager(originalSecurityManager); private final NoExitSecurityManager managerWithoutOriginal = new NoExitSecurityManager(null); @Test From 26e344d9adbdbd06e3bc827ebbb0b785cf8794ad Mon Sep 17 00:00:00 2001 From: englishman Date: Thu, 29 Sep 2016 16:56:49 -0400 Subject: [PATCH 04/15] Rework synch latch --- .../internal/NoExitSecurityManager.java | 22 ++++++++++-------- .../internal/synch/EmptySynchLatch.java | 10 ++++++++ .../system/internal/synch/SynchLatch.java | 6 +++++ .../synch/SynchLatchWithWainting.java | 23 +++++++++++++++++++ 4 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/junit/contrib/java/lang/system/internal/synch/EmptySynchLatch.java create mode 100644 src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatch.java create mode 100644 src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatchWithWainting.java diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java index 2797ee2b..097da0ed 100644 --- a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java +++ b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java @@ -1,10 +1,12 @@ package org.junit.contrib.java.lang.system.internal; +import org.junit.contrib.java.lang.system.internal.synch.EmptySynchLatch; +import org.junit.contrib.java.lang.system.internal.synch.SynchLatch; +import org.junit.contrib.java.lang.system.internal.synch.SynchLatchWithWainting; + import java.io.FileDescriptor; import java.net.InetAddress; import java.security.Permission; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; /** * A {@code NoExitSecurityManager} throws a {@link CheckExitCalled} exception @@ -14,31 +16,33 @@ public class NoExitSecurityManager extends SecurityManager { private final SecurityManager originalSecurityManager; - private final long timeout; + private final SynchLatch latch; - private final CountDownLatch synchLatch = new CountDownLatch(1); private Integer statusOfFirstExitCall = null; public NoExitSecurityManager(SecurityManager originalSecurityManager) { - this(originalSecurityManager, 0); + this(originalSecurityManager, new EmptySynchLatch()); } public NoExitSecurityManager(SecurityManager originalSecurityManager, long timeout) { + this(originalSecurityManager, new SynchLatchWithWainting(timeout)); + } + + private NoExitSecurityManager(SecurityManager originalSecurityManager, SynchLatch latch) { this.originalSecurityManager = originalSecurityManager; - this.timeout = timeout; + this.latch = latch; } @Override public void checkExit(int status) { if (statusOfFirstExitCall == null) statusOfFirstExitCall = status; - synchLatch.countDown(); + latch.goAhead(); throw new CheckExitCalled(status); } public boolean isCheckExitCalled() throws InterruptedException { - if (timeout > 0) - synchLatch.await(timeout, TimeUnit.MILLISECONDS); + latch.await(); return statusOfFirstExitCall != null; } diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/synch/EmptySynchLatch.java b/src/main/java/org/junit/contrib/java/lang/system/internal/synch/EmptySynchLatch.java new file mode 100644 index 00000000..67985393 --- /dev/null +++ b/src/main/java/org/junit/contrib/java/lang/system/internal/synch/EmptySynchLatch.java @@ -0,0 +1,10 @@ +package org.junit.contrib.java.lang.system.internal.synch; + +public final class EmptySynchLatch implements SynchLatch { + + public void goAhead() { + } + + public void await() throws InterruptedException { + } +} diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatch.java b/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatch.java new file mode 100644 index 00000000..86f4f20e --- /dev/null +++ b/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatch.java @@ -0,0 +1,6 @@ +package org.junit.contrib.java.lang.system.internal.synch; + +public interface SynchLatch { + void goAhead(); + void await() throws InterruptedException; +} diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatchWithWainting.java b/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatchWithWainting.java new file mode 100644 index 00000000..16dc5f7d --- /dev/null +++ b/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatchWithWainting.java @@ -0,0 +1,23 @@ +package org.junit.contrib.java.lang.system.internal.synch; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public final class SynchLatchWithWainting implements SynchLatch { + + private final long timeout; + private final CountDownLatch latch; + + public SynchLatchWithWainting(long timeout) { + this.timeout = timeout; + latch = new CountDownLatch(1); + } + + public void goAhead() { + latch.countDown(); + } + + public void await() throws InterruptedException { + latch.await(timeout, TimeUnit.MILLISECONDS); + } +} From cc6afe419cbb575902b494f32e29c1a24ca093aa Mon Sep 17 00:00:00 2001 From: englishman Date: Wed, 2 Nov 2016 17:03:49 -0400 Subject: [PATCH 05/15] Simplify code keeping in mind CountDownLatch contract --- .../java/lang/system/internal/NoExitSecurityManager.java | 7 +------ .../lang/system/internal/NoExitSecurityManagerTest.java | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java index 2797ee2b..1aa4e1ee 100644 --- a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java +++ b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java @@ -19,10 +19,6 @@ public class NoExitSecurityManager extends SecurityManager { private final CountDownLatch synchLatch = new CountDownLatch(1); private Integer statusOfFirstExitCall = null; - public NoExitSecurityManager(SecurityManager originalSecurityManager) { - this(originalSecurityManager, 0); - } - public NoExitSecurityManager(SecurityManager originalSecurityManager, long timeout) { this.originalSecurityManager = originalSecurityManager; this.timeout = timeout; @@ -37,8 +33,7 @@ public void checkExit(int status) { } public boolean isCheckExitCalled() throws InterruptedException { - if (timeout > 0) - synchLatch.await(timeout, TimeUnit.MILLISECONDS); + synchLatch.await(timeout, TimeUnit.MILLISECONDS); return statusOfFirstExitCall != null; } diff --git a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java index 81e0c5b2..cd12e171 100644 --- a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java +++ b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java @@ -20,8 +20,8 @@ public class NoExitSecurityManagerTest { public final TemporaryFolder temporaryFolder = new TemporaryFolder(); private final SecurityManager originalSecurityManager = mock(SecurityManager.class); - private final NoExitSecurityManager managerWithOriginal = new NoExitSecurityManager(originalSecurityManager); - private final NoExitSecurityManager managerWithoutOriginal = new NoExitSecurityManager(null); + private final NoExitSecurityManager managerWithOriginal = new NoExitSecurityManager(originalSecurityManager, 0); + private final NoExitSecurityManager managerWithoutOriginal = new NoExitSecurityManager(null, 0); @Test public void an_exception_with_the_status_is_thrown_when_checkExit_is_called() { From 368b566d4b4195a040f4479ad573c270da1fc2bf Mon Sep 17 00:00:00 2001 From: englishman Date: Wed, 2 Nov 2016 17:12:24 -0400 Subject: [PATCH 06/15] Remove unneeded files --- .../internal/NoExitSecurityManagerTest.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java index cd12e171..d0058971 100644 --- a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java +++ b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java @@ -1,17 +1,19 @@ package org.junit.contrib.java.lang.system.internal; -import com.github.stefanbirkner.fishbowl.Statement; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import static com.github.stefanbirkner.fishbowl.Fishbowl.exceptionThrownBy; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.io.FileDescriptor; import java.net.InetAddress; import java.security.Permission; -import static com.github.stefanbirkner.fishbowl.Fishbowl.exceptionThrownBy; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; +import com.github.stefanbirkner.fishbowl.Statement; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; public class NoExitSecurityManagerTest { private static final int DUMMY_STATUS = 1; From a4d86e1f5bc99de16543a6f9a19bcf2c11276d0e Mon Sep 17 00:00:00 2001 From: englishman Date: Wed, 2 Nov 2016 17:13:13 -0400 Subject: [PATCH 07/15] Remove unneeded files 2 --- .../internal/synch/EmptySynchLatch.java | 10 -------- .../system/internal/synch/SynchLatch.java | 6 ----- .../synch/SynchLatchWithWainting.java | 23 ------------------- 3 files changed, 39 deletions(-) delete mode 100644 src/main/java/org/junit/contrib/java/lang/system/internal/synch/EmptySynchLatch.java delete mode 100644 src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatch.java delete mode 100644 src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatchWithWainting.java diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/synch/EmptySynchLatch.java b/src/main/java/org/junit/contrib/java/lang/system/internal/synch/EmptySynchLatch.java deleted file mode 100644 index 67985393..00000000 --- a/src/main/java/org/junit/contrib/java/lang/system/internal/synch/EmptySynchLatch.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.junit.contrib.java.lang.system.internal.synch; - -public final class EmptySynchLatch implements SynchLatch { - - public void goAhead() { - } - - public void await() throws InterruptedException { - } -} diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatch.java b/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatch.java deleted file mode 100644 index 86f4f20e..00000000 --- a/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatch.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.junit.contrib.java.lang.system.internal.synch; - -public interface SynchLatch { - void goAhead(); - void await() throws InterruptedException; -} diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatchWithWainting.java b/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatchWithWainting.java deleted file mode 100644 index 16dc5f7d..00000000 --- a/src/main/java/org/junit/contrib/java/lang/system/internal/synch/SynchLatchWithWainting.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.junit.contrib.java.lang.system.internal.synch; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -public final class SynchLatchWithWainting implements SynchLatch { - - private final long timeout; - private final CountDownLatch latch; - - public SynchLatchWithWainting(long timeout) { - this.timeout = timeout; - latch = new CountDownLatch(1); - } - - public void goAhead() { - latch.countDown(); - } - - public void await() throws InterruptedException { - latch.await(timeout, TimeUnit.MILLISECONDS); - } -} From b3ad88f889442f171549e2444db5a89ff26bc632 Mon Sep 17 00:00:00 2001 From: englishman Date: Wed, 2 Nov 2016 17:57:08 -0400 Subject: [PATCH 08/15] Make check strict and avoid FindBug error --- .../java/lang/system/internal/NoExitSecurityManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java index 1aa4e1ee..1c0e0587 100644 --- a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java +++ b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java @@ -33,8 +33,8 @@ public void checkExit(int status) { } public boolean isCheckExitCalled() throws InterruptedException { - synchLatch.await(timeout, TimeUnit.MILLISECONDS); - return statusOfFirstExitCall != null; + boolean wasCountDown = synchLatch.await(timeout, TimeUnit.MILLISECONDS); + return statusOfFirstExitCall != null && wasCountDown; } public int getStatusOfFirstCheckExitCall() throws InterruptedException { From 6ecda12c3fabf0d3494cc3fbd546d10d0eb064a3 Mon Sep 17 00:00:00 2001 From: englishman Date: Thu, 3 Nov 2016 11:10:40 -0400 Subject: [PATCH 09/15] Simplify latch usage --- .../java/lang/system/internal/NoExitSecurityManager.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java index 1c0e0587..c577c4c9 100644 --- a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java +++ b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java @@ -16,7 +16,7 @@ public class NoExitSecurityManager extends SecurityManager { private final SecurityManager originalSecurityManager; private final long timeout; - private final CountDownLatch synchLatch = new CountDownLatch(1); + private final CountDownLatch exitLatch = new CountDownLatch(1); private Integer statusOfFirstExitCall = null; public NoExitSecurityManager(SecurityManager originalSecurityManager, long timeout) { @@ -28,13 +28,12 @@ public NoExitSecurityManager(SecurityManager originalSecurityManager, long timeo public void checkExit(int status) { if (statusOfFirstExitCall == null) statusOfFirstExitCall = status; - synchLatch.countDown(); + exitLatch.countDown(); throw new CheckExitCalled(status); } public boolean isCheckExitCalled() throws InterruptedException { - boolean wasCountDown = synchLatch.await(timeout, TimeUnit.MILLISECONDS); - return statusOfFirstExitCall != null && wasCountDown; + return exitLatch.await(timeout, TimeUnit.MILLISECONDS); } public int getStatusOfFirstCheckExitCall() throws InterruptedException { From ccb090b4436461d0cc86f213ce9a1bfdc2fffa29 Mon Sep 17 00:00:00 2001 From: englishman Date: Thu, 3 Nov 2016 11:27:18 -0400 Subject: [PATCH 10/15] Avoid InterruptedException hiding --- .../java/lang/system/ExpectedSystemExit.java | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java index 57ad1fa0..f02f44a4 100644 --- a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java +++ b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java @@ -1,18 +1,18 @@ package org.junit.contrib.java.lang.system; +import static java.lang.System.getSecurityManager; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.Collection; + import org.junit.contrib.java.lang.system.internal.CheckExitCalled; import org.junit.contrib.java.lang.system.internal.NoExitSecurityManager; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; -import java.util.ArrayList; -import java.util.Collection; - -import static java.lang.System.getSecurityManager; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - /** * The {@code ExpectedSystemExit} allows in-test specification of expected * {@code System.exit(...)} calls. @@ -140,16 +140,12 @@ public void evaluate() throws Throwable { }; } - private void checkSystemExit() { + private void checkSystemExit() throws InterruptedException { NoExitSecurityManager securityManager = (NoExitSecurityManager) getSecurityManager(); - try { - if (securityManager.isCheckExitCalled()) - handleSystemExitWithStatus(securityManager.getStatusOfFirstCheckExitCall()); - else - handleMissingSystemExit(); - } catch (InterruptedException e) { - fail("Unexpected waiting loop break."); - } + if (securityManager.isCheckExitCalled()) + handleSystemExitWithStatus(securityManager.getStatusOfFirstCheckExitCall()); + else + handleMissingSystemExit(); } private void handleMissingSystemExit() { From 1b3c0ee73104ff2e03d0c8cbeccf77c2a7077645 Mon Sep 17 00:00:00 2001 From: englishman Date: Thu, 3 Nov 2016 11:39:40 -0400 Subject: [PATCH 11/15] Update java doc text --- .../contrib/java/lang/system/ExpectedSystemExit.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java index f02f44a4..0eb5fd36 100644 --- a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java +++ b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java @@ -28,7 +28,9 @@ *

* Some care must be taken if your system under test creates a new thread and * this thread calls {@code System.exit()}. In this case you have to ensure that - * the test does not finish before {@code System.exit()} is called. + * the test does not finish before {@code System.exit()} is called. Use + * {@code ExpectedSystemExit.timeout(...)} method call to specify test method + * waiting time in milliseconds. * *

  * public class AppWithExit {
@@ -78,6 +80,13 @@
  *   }
  *
  *   @Test
+ *   public void systemExitInSeparateThread() {
+ *     exit.expectSystemExitWithStatus(1);
+ *     exit.timeout(1000);
+ *     AppWithExit.doSomethingInSeparateThreadAndExit();
+ *   }
+ *
+ *   @Test
  *   public void noSystemExit() {
  *     AppWithExit.doNothing();
  *     //passes

From 557a17d1f0c0a4908b4f319082c9c219676069fa Mon Sep 17 00:00:00 2001
From: englishman 
Date: Fri, 4 Nov 2016 12:23:09 -0400
Subject: [PATCH 12/15] Simplify NoExitSecurityManager API

---
 .../java/lang/system/ExpectedSystemExit.java  |  5 +++--
 .../internal/NoExitSecurityManager.java       | 12 ++++------
 .../internal/NoExitSecurityManagerTest.java   | 22 ++-----------------
 3 files changed, 9 insertions(+), 30 deletions(-)

diff --git a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java
index 0eb5fd36..4ff2e7b3 100644
--- a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java
+++ b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java
@@ -151,8 +151,9 @@ public void evaluate() throws Throwable {
 
 	private void checkSystemExit() throws InterruptedException {
 		NoExitSecurityManager securityManager = (NoExitSecurityManager) getSecurityManager();
-		if (securityManager.isCheckExitCalled())
-			handleSystemExitWithStatus(securityManager.getStatusOfFirstCheckExitCall());
+		Integer exitStatus = securityManager.getStatusOfFirstCheckExitCall();
+		if (exitStatus != null)
+			handleSystemExitWithStatus(exitStatus);
 		else
 			handleMissingSystemExit();
 	}
diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
index c577c4c9..31c339dc 100644
--- a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
+++ b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
@@ -32,15 +32,11 @@ public void checkExit(int status) {
 		throw new CheckExitCalled(status);
 	}
 
-	public boolean isCheckExitCalled() throws InterruptedException {
-		return exitLatch.await(timeout, TimeUnit.MILLISECONDS);
-	}
-
-	public int getStatusOfFirstCheckExitCall() throws InterruptedException {
-		if (isCheckExitCalled())
+	public Integer getStatusOfFirstCheckExitCall() throws InterruptedException {
+		if (statusOfFirstExitCall != null)
 			return statusOfFirstExitCall;
-		else
-			throw new IllegalStateException("checkExit(int) has not been called.");
+		exitLatch.await(timeout, TimeUnit.MILLISECONDS);
+		return statusOfFirstExitCall;
 	}
 
 	@Override
diff --git a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java
index d0058971..30c664a0 100644
--- a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java
+++ b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java
@@ -438,17 +438,6 @@ public void getThreadGroup_may_be_called_without_original_security_manager() {
 		managerWithoutOriginal.getThreadGroup();
 	}
 
-	@Test
-	public void information_about_a_missing_checkExit_call_is_available() throws InterruptedException {
-		assertThat(managerWithOriginal.isCheckExitCalled()).isFalse();
-	}
-
-	@Test
-	public void information_about_a_checkExit_call_is_available() throws InterruptedException {
-		safeCallCheckExitWithStatus(DUMMY_STATUS);
-		assertThat(managerWithOriginal.isCheckExitCalled()).isTrue();
-	}
-
 	@Test
 	public void status_of_first_call_of_checkExit_is_available() throws InterruptedException {
 		safeCallCheckExitWithStatus(DUMMY_STATUS);
@@ -465,14 +454,7 @@ private void safeCallCheckExitWithStatus(int status) {
 	}
 
 	@Test
-	public void fails_to_provide_status_of_first_checkExit_call_if_this_call_did_not_happen() {
-		Throwable exception = exceptionThrownBy(new Statement() {
-			public void evaluate() throws Throwable {
-				managerWithOriginal.getStatusOfFirstCheckExitCall();
-			}
-		});
-		assertThat(exception)
-			.isInstanceOf(IllegalStateException.class)
-			.hasMessage("checkExit(int) has not been called.");
+	public void null_status_of_first_checkExit_call_if_this_call_did_not_happen() throws InterruptedException {
+		assertThat(managerWithOriginal.getStatusOfFirstCheckExitCall()).isNull();
 	}
 }

From 104514c0fd3f0ed0e2b3b7c6f86e447d68a26f06 Mon Sep 17 00:00:00 2001
From: englishman 
Date: Fri, 4 Nov 2016 12:25:34 -0400
Subject: [PATCH 13/15] NoExitSecurityManager minor refactoring

---
 .../java/lang/system/internal/NoExitSecurityManager.java | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
index 31c339dc..d77eab0c 100644
--- a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
+++ b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
@@ -47,7 +47,8 @@ public boolean getInCheck() {
 
 	@Override
 	public Object getSecurityContext() {
-		return (originalSecurityManager == null) ? super.getSecurityContext()
+		return originalSecurityManager == null
+			? super.getSecurityContext()
 			: originalSecurityManager.getSecurityContext();
 	}
 
@@ -179,7 +180,8 @@ public void checkPropertyAccess(String key) {
 
 	@Override
 	public boolean checkTopLevelWindow(Object window) {
-		return (originalSecurityManager == null) ? super.checkTopLevelWindow(window)
+		return originalSecurityManager == null
+			? super.checkTopLevelWindow(window)
 			: originalSecurityManager.checkTopLevelWindow(window);
 	}
 
@@ -233,7 +235,8 @@ public void checkSecurityAccess(String target) {
 
 	@Override
 	public ThreadGroup getThreadGroup() {
-		return (originalSecurityManager == null) ? super.getThreadGroup()
+		return originalSecurityManager == null
+			? super.getThreadGroup()
 			: originalSecurityManager.getThreadGroup();
 	}
 }

From a63c64863d685ae053def10ae247085b2781fde3 Mon Sep 17 00:00:00 2001
From: englishman 
Date: Fri, 4 Nov 2016 12:28:22 -0400
Subject: [PATCH 14/15] Remove timeout form NoExitSecurityManager

---
 .../contrib/java/lang/system/ExpectedSystemExit.java      | 4 ++--
 .../java/lang/system/internal/NoExitSecurityManager.java  | 8 +++-----
 .../lang/system/internal/NoExitSecurityManagerTest.java   | 8 ++++----
 3 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java
index 4ff2e7b3..12e0ceb3 100644
--- a/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java
+++ b/src/main/java/org/junit/contrib/java/lang/system/ExpectedSystemExit.java
@@ -131,7 +131,7 @@ public Statement apply(final Statement base, Description description) {
 	}
 
 	private ProvideSecurityManager createNoExitSecurityManagerRule() {
-		SecurityManager noExitSecurityManager = new NoExitSecurityManager(getSecurityManager(), timeout);
+		SecurityManager noExitSecurityManager = new NoExitSecurityManager(getSecurityManager());
 		return new ProvideSecurityManager(noExitSecurityManager);
 	}
 
@@ -151,7 +151,7 @@ public void evaluate() throws Throwable {
 
 	private void checkSystemExit() throws InterruptedException {
 		NoExitSecurityManager securityManager = (NoExitSecurityManager) getSecurityManager();
-		Integer exitStatus = securityManager.getStatusOfFirstCheckExitCall();
+		Integer exitStatus = securityManager.getStatusOfFirstCheckExitCall(timeout);
 		if (exitStatus != null)
 			handleSystemExitWithStatus(exitStatus);
 		else
diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
index d77eab0c..08e865fc 100644
--- a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
+++ b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
@@ -14,14 +14,12 @@
 public class NoExitSecurityManager extends SecurityManager {
 
 	private final SecurityManager originalSecurityManager;
-	private final long timeout;
-
 	private final CountDownLatch exitLatch = new CountDownLatch(1);
+
 	private Integer statusOfFirstExitCall = null;
 
-	public NoExitSecurityManager(SecurityManager originalSecurityManager, long timeout) {
+	public NoExitSecurityManager(SecurityManager originalSecurityManager) {
 		this.originalSecurityManager = originalSecurityManager;
-		this.timeout = timeout;
 	}
 
 	@Override
@@ -32,7 +30,7 @@ public void checkExit(int status) {
 		throw new CheckExitCalled(status);
 	}
 
-	public Integer getStatusOfFirstCheckExitCall() throws InterruptedException {
+	public Integer getStatusOfFirstCheckExitCall(long timeout) throws InterruptedException {
 		if (statusOfFirstExitCall != null)
 			return statusOfFirstExitCall;
 		exitLatch.await(timeout, TimeUnit.MILLISECONDS);
diff --git a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java
index 30c664a0..40d73850 100644
--- a/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java
+++ b/src/test/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManagerTest.java
@@ -22,8 +22,8 @@ public class NoExitSecurityManagerTest {
 	public final TemporaryFolder temporaryFolder = new TemporaryFolder();
 
 	private final SecurityManager originalSecurityManager = mock(SecurityManager.class);
-	private final NoExitSecurityManager managerWithOriginal = new NoExitSecurityManager(originalSecurityManager, 0);
-	private final NoExitSecurityManager managerWithoutOriginal = new NoExitSecurityManager(null, 0);
+	private final NoExitSecurityManager managerWithOriginal = new NoExitSecurityManager(originalSecurityManager);
+	private final NoExitSecurityManager managerWithoutOriginal = new NoExitSecurityManager(null);
 
 	@Test
 	public void an_exception_with_the_status_is_thrown_when_checkExit_is_called() {
@@ -442,7 +442,7 @@ public void getThreadGroup_may_be_called_without_original_security_manager() {
 	public void status_of_first_call_of_checkExit_is_available() throws InterruptedException {
 		safeCallCheckExitWithStatus(DUMMY_STATUS);
 		safeCallCheckExitWithStatus(DUMMY_STATUS + 1);
-		assertThat(managerWithOriginal.getStatusOfFirstCheckExitCall())
+		assertThat(managerWithOriginal.getStatusOfFirstCheckExitCall(0))
 			.isEqualTo(DUMMY_STATUS);
 	}
 
@@ -455,6 +455,6 @@ private void safeCallCheckExitWithStatus(int status) {
 
 	@Test
 	public void null_status_of_first_checkExit_call_if_this_call_did_not_happen() throws InterruptedException {
-		assertThat(managerWithOriginal.getStatusOfFirstCheckExitCall()).isNull();
+		assertThat(managerWithOriginal.getStatusOfFirstCheckExitCall(0)).isNull();
 	}
 }

From ef13c1a9aa1600a998fa4f4ab68702c167b6a0ce Mon Sep 17 00:00:00 2001
From: englishman 
Date: Fri, 4 Nov 2016 12:53:11 -0400
Subject: [PATCH 15/15] Make code thread safe; remove manual flow execution via
 CountDownLatch

---
 .../system/internal/NoExitSecurityManager.java   | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

diff --git a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
index 08e865fc..b35f6761 100644
--- a/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
+++ b/src/main/java/org/junit/contrib/java/lang/system/internal/NoExitSecurityManager.java
@@ -3,7 +3,8 @@
 import java.io.FileDescriptor;
 import java.net.InetAddress;
 import java.security.Permission;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -14,9 +15,7 @@
 public class NoExitSecurityManager extends SecurityManager {
 
 	private final SecurityManager originalSecurityManager;
-	private final CountDownLatch exitLatch = new CountDownLatch(1);
-
-	private Integer statusOfFirstExitCall = null;
+	private final BlockingQueue exitStatusHolder = new LinkedBlockingQueue(1);
 
 	public NoExitSecurityManager(SecurityManager originalSecurityManager) {
 		this.originalSecurityManager = originalSecurityManager;
@@ -24,17 +23,12 @@ public NoExitSecurityManager(SecurityManager originalSecurityManager) {
 
 	@Override
 	public void checkExit(int status) {
-		if (statusOfFirstExitCall == null)
-			statusOfFirstExitCall = status;
-		exitLatch.countDown();
+		exitStatusHolder.offer(status);
 		throw new CheckExitCalled(status);
 	}
 
 	public Integer getStatusOfFirstCheckExitCall(long timeout) throws InterruptedException {
-		if (statusOfFirstExitCall != null)
-			return statusOfFirstExitCall;
-		exitLatch.await(timeout, TimeUnit.MILLISECONDS);
-		return statusOfFirstExitCall;
+		return exitStatusHolder.poll(timeout, TimeUnit.MILLISECONDS);
 	}
 
 	@Override