Changed prompt interruption contract
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
This commit is contained in:
parent
c4bbfd8434
commit
c206b5b22c
@ -56,6 +56,11 @@
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.bigeon.test</groupId>
|
||||
<artifactId>junitmt</artifactId>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<reporting>
|
||||
<plugins>
|
||||
|
@ -8,18 +8,19 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import net.bigeon.gclc.manager.ConsoleInput;
|
||||
import net.bigeon.test.junitmt.FunctionalTestRunnable;
|
||||
import net.bigeon.test.junitmt.TestFunction;
|
||||
import net.bigeon.test.junitmt.ThreadTest;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bigeon
|
||||
*
|
||||
*/
|
||||
/** @author Emmanuel Bigeon */
|
||||
public final class InputContract {
|
||||
|
||||
public void testInputContract(final Supplier<ConsoleInput> inputs)
|
||||
throws IOException {
|
||||
throws IOException, InterruptedException {
|
||||
// Test close contract
|
||||
final ConsoleInput input = inputs.get();
|
||||
assertFalse("An input should not initially be closed", input.isClosed());
|
||||
@ -33,5 +34,29 @@ public final class InputContract {
|
||||
} catch (final IOException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
// Test interruption contract
|
||||
final ConsoleInput input2 = inputs.get();
|
||||
final FunctionalTestRunnable prompting = new FunctionalTestRunnable(
|
||||
new TestFunction() {
|
||||
|
||||
@Override
|
||||
public void apply() throws Exception {
|
||||
try {
|
||||
input2.prompt();
|
||||
fail("Interrupted prompt should throw INterruptedIOException");
|
||||
} catch (final InterruptedIOException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
});
|
||||
final Thread th = new Thread(prompting);
|
||||
th.start();
|
||||
// while (!input2.isPrompting()) {
|
||||
th.join(200);
|
||||
//
|
||||
// }
|
||||
input2.interruptPrompt();
|
||||
ThreadTest.assertRuns(th, prompting);
|
||||
}
|
||||
}
|
||||
|
@ -94,8 +94,7 @@ public interface ConsoleInput extends AutoCloseable {
|
||||
/** Indicate to the input that is should interrompt the prompting, if possible.
|
||||
* <p>
|
||||
* The pending {@link #prompt()} or {@link #prompt(String)} operations should
|
||||
* return immediatly. However the returned value can be anything (from the
|
||||
* partial prompt content to an empty string or even a null pointer). */
|
||||
* return immediately by throwing an InterruptedIOException. */
|
||||
void interruptPrompt();
|
||||
|
||||
/** Test if the input is closed.
|
||||
|
@ -111,6 +111,7 @@ public final class ReadingRunnable implements Runnable {
|
||||
private final Map<String, Object> messageBlocker = new ConcurrentHashMap<>();
|
||||
/** The lock. */
|
||||
private final Object messageBlockerLock = new Object();
|
||||
private boolean interrupting = false;
|
||||
/** Create a reading runnable.
|
||||
*
|
||||
* @param reader the input to read from */
|
||||
@ -146,8 +147,7 @@ public final class ReadingRunnable implements Runnable {
|
||||
public String getMessage() throws IOException {
|
||||
synchronized (lock) {
|
||||
if (!messages.isEmpty()) {
|
||||
notifyMessage(messages.peek());
|
||||
return messages.poll();
|
||||
return pollMessage();
|
||||
}
|
||||
if (!running) {
|
||||
throw new IOException(CLOSED_PIPE);
|
||||
@ -156,8 +156,7 @@ public final class ReadingRunnable implements Runnable {
|
||||
waitMessage(TIMEOUT);
|
||||
LOGGER.log(Level.FINEST, "Polled: {0}", messages.peek()); //$NON-NLS-1$
|
||||
waiting = false;
|
||||
notifyMessage(messages.peek());
|
||||
return messages.poll();
|
||||
return pollMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,8 +168,7 @@ public final class ReadingRunnable implements Runnable {
|
||||
public String getNextMessage(final long timeout) throws IOException {
|
||||
synchronized (lock) {
|
||||
if (!messages.isEmpty()) {
|
||||
notifyMessage(messages.peek());
|
||||
return messages.poll();
|
||||
return pollMessage();
|
||||
}
|
||||
if (!running) {
|
||||
throw new IOException(CLOSED_PIPE);
|
||||
@ -181,10 +179,18 @@ public final class ReadingRunnable implements Runnable {
|
||||
if (messages.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
notifyMessage(messages.peek());
|
||||
return messages.poll();
|
||||
return pollMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private String pollMessage() throws InterruptedIOException {
|
||||
final String msg = messages.poll();
|
||||
if (msg.isEmpty() && interrupting) {
|
||||
throw new InterruptedIOException();
|
||||
}
|
||||
notifyMessage(msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/** Wait for a message to be delivered.
|
||||
*
|
||||
@ -221,7 +227,8 @@ public final class ReadingRunnable implements Runnable {
|
||||
public void interrupt() {
|
||||
synchronized (lock) {
|
||||
if (waiting) {
|
||||
messages.offer(""); //$NON-NLS-1$
|
||||
messages.offer("");
|
||||
interrupting = true;
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
@ -90,6 +91,9 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.bigeon.gclc.utils.ReadingRunnable;
|
||||
import net.bigeon.test.junitmt.FunctionalTestRunnable;
|
||||
import net.bigeon.test.junitmt.TestFunction;
|
||||
import net.bigeon.test.junitmt.ThreadTest;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@ -198,9 +202,11 @@ public class ReadingRunnableTest {
|
||||
public final void testGetPendingMessages() throws IOException, InterruptedException {
|
||||
|
||||
final PipedOutputStream out = new PipedOutputStream();
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(new PipedInputStream(out), StandardCharsets.UTF_8));
|
||||
final BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(new PipedInputStream(out), StandardCharsets.UTF_8));
|
||||
final ReadingRunnable runnable = new ReadingRunnable(reader);
|
||||
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
|
||||
final BufferedWriter writer = new BufferedWriter(
|
||||
new OutputStreamWriter(out, StandardCharsets.UTF_8));
|
||||
writer.write("one");
|
||||
writer.newLine();
|
||||
writer.write("two");
|
||||
@ -222,4 +228,30 @@ public class ReadingRunnableTest {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterruption() throws IOException, InterruptedException {
|
||||
final PipedOutputStream out = new PipedOutputStream();
|
||||
final BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(new PipedInputStream(out), StandardCharsets.UTF_8));
|
||||
final ReadingRunnable runnable = new ReadingRunnable(reader);
|
||||
|
||||
final FunctionalTestRunnable test = new FunctionalTestRunnable(
|
||||
new TestFunction() {
|
||||
@Override
|
||||
public void apply() throws IOException {
|
||||
try {
|
||||
runnable.getMessage();
|
||||
fail("Message interruption should cause an exception");
|
||||
} catch (final InterruptedIOException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
});
|
||||
final Thread th = new Thread(test, "Prompt wait");
|
||||
th.start();
|
||||
th.join(200);
|
||||
runnable.interrupt();
|
||||
ThreadTest.assertRuns(th, test);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user