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..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 @@ -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
@@ -93,6 +102,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 +116,10 @@ public void expectSystemExit() {
expectExit = true;
}
+ public void timeout(long timeout) {
+ this.timeout = timeout;
+ }
+
public void checkAssertionAfterwards(Assertion assertion) {
assertions.add(assertion);
}
@@ -117,8 +131,7 @@ public Statement apply(final Statement base, Description description) {
}
private ProvideSecurityManager createNoExitSecurityManagerRule() {
- NoExitSecurityManager noExitSecurityManager = new NoExitSecurityManager(
- getSecurityManager());
+ SecurityManager noExitSecurityManager = new NoExitSecurityManager(getSecurityManager());
return new ProvideSecurityManager(noExitSecurityManager);
}
@@ -136,10 +149,11 @@ public void evaluate() throws Throwable {
};
}
- private void checkSystemExit() {
+ private void checkSystemExit() throws InterruptedException {
NoExitSecurityManager securityManager = (NoExitSecurityManager) getSecurityManager();
- if (securityManager.isCheckExitCalled())
- handleSystemExitWithStatus(securityManager.getStatusOfFirstCheckExitCall());
+ Integer exitStatus = securityManager.getStatusOfFirstCheckExitCall(timeout);
+ 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 df7fee45..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,6 +3,9 @@
import java.io.FileDescriptor;
import java.net.InetAddress;
import java.security.Permission;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
/**
* A {@code NoExitSecurityManager} throws a {@link CheckExitCalled} exception
@@ -10,8 +13,9 @@
* delegated to the original security manager.
*/
public class NoExitSecurityManager extends SecurityManager {
+
private final SecurityManager originalSecurityManager;
- private Integer statusOfFirstExitCall = null;
+ private final BlockingQueue exitStatusHolder = new LinkedBlockingQueue(1);
public NoExitSecurityManager(SecurityManager originalSecurityManager) {
this.originalSecurityManager = originalSecurityManager;
@@ -19,21 +23,12 @@ public NoExitSecurityManager(SecurityManager originalSecurityManager) {
@Override
public void checkExit(int status) {
- if (statusOfFirstExitCall == null)
- statusOfFirstExitCall = status;
+ exitStatusHolder.offer(status);
throw new CheckExitCalled(status);
}
- public boolean isCheckExitCalled() {
- return statusOfFirstExitCall != null;
- }
-
- public int getStatusOfFirstCheckExitCall() {
- if (isCheckExitCalled())
- return statusOfFirstExitCall;
- else
- throw new IllegalStateException(
- "checkExit(int) has not been called.");
+ public Integer getStatusOfFirstCheckExitCall(long timeout) throws InterruptedException {
+ return exitStatusHolder.poll(timeout, TimeUnit.MILLISECONDS);
}
@Override
@@ -44,7 +39,8 @@ public boolean getInCheck() {
@Override
public Object getSecurityContext() {
- return (originalSecurityManager == null) ? super.getSecurityContext()
+ return originalSecurityManager == null
+ ? super.getSecurityContext()
: originalSecurityManager.getSecurityContext();
}
@@ -176,7 +172,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);
}
@@ -230,7 +227,8 @@ public void checkSecurityAccess(String target) {
@Override
public ThreadGroup getThreadGroup() {
- return (originalSecurityManager == null) ? super.getThreadGroup()
+ return originalSecurityManager == null
+ ? super.getThreadGroup()
: originalSecurityManager.getThreadGroup();
}
}
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..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
@@ -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,8 @@ 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);
+ rule.timeout(1000);
+ executeTestWithRule(new SystemExitInSeparateThread(), rule);
}
private static class SystemExit0 extends Statement {
@@ -145,17 +144,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(500);
System.exit(ARBITRARY_EXIT_STATUS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
}
- };
- Thread thread = new Thread(callSystemExit);
- thread.start();
- sleep(1000); // wait until the thread exits
+ }
}
}
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..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,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
@@ -440,21 +439,10 @@ public void getThreadGroup_may_be_called_without_original_security_manager() {
}
@Test
- public void information_about_a_missing_checkExit_call_is_available() {
- assertThat(managerWithOriginal.isCheckExitCalled()).isFalse();
- }
-
- @Test
- public void information_about_a_checkExit_call_is_available() {
- 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())
+ assertThat(managerWithOriginal.getStatusOfFirstCheckExitCall(0))
.isEqualTo(DUMMY_STATUS);
}
@@ -466,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(0)).isNull();
}
}