blob: e15ad27eb62b60a6b5b92aaaa5e3e21caadf17eb [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.harmony.nio.tests.java.nio.channels;
import dalvik.annotation.TestLevel;
import dalvik.annotation.TestTargetClass;
import dalvik.annotation.TestTargetNew;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.Pipe;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import junit.framework.TestCase;
import tests.support.Support_PortManager;
/*
* Tests for Selector and its default implementation
*/
@TestTargetClass(
value = Selector.class,
untestedMethods = {
@TestTargetNew(
level = TestLevel.NOT_NECESSARY,
notes = "empty protected constructor",
method = "Selector",
args = {}
)
}
)
public class SelectorTest extends TestCase {
private static final int WAIT_TIME = 100;
private static final int PORT = Support_PortManager.getNextPort();
private static final InetSocketAddress LOCAL_ADDRESS = new InetSocketAddress(
"127.0.0.1", PORT);
Selector selector;
ServerSocketChannel ssc;
enum SelectType {
NULL, TIMEOUT, NOW
}
protected void setUp() throws Exception {
super.setUp();
ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ServerSocket ss = ssc.socket();
InetSocketAddress address = new InetSocketAddress(PORT);
ss.bind(address);
selector = Selector.open();
}
protected void tearDown() throws Exception {
try {
ssc.close();
} catch (Exception e) {
// do nothing
}
try {
selector.close();
} catch (Exception e) {
// do nothing
}
super.tearDown();
}
/**
* @tests java.nio.channels.Selector#open()
*/
@TestTargetNew(
level = TestLevel.COMPLETE,
notes = "",
method = "open",
args = {}
)
public void test_open() throws IOException {
assertNotNull(selector);
}
/**
* @tests Selector#isOpen()
*/
@TestTargetNew(
level = TestLevel.COMPLETE,
notes = "",
method = "isOpen",
args = {}
)
public void test_isOpen() throws IOException {
assertTrue(selector.isOpen());
selector.close();
assertFalse(selector.isOpen());
}
/**
* @tests java.nio.channels.Selector#provider()
*/
@TestTargetNew(
level = TestLevel.COMPLETE,
notes = "",
method = "provider",
args = {}
)
public void test_provider() throws IOException {
// should be system default provider
assertNotNull(selector.provider());
assertSame(SelectorProvider.provider(), selector.provider());
}
/**
* @tests java.nio.channels.Selector#keys()
*/
@TestTargetNew(
level = TestLevel.COMPLETE,
notes = "",
method = "keys",
args = {}
)
public void test_keys() throws IOException {
SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
Set<SelectionKey> keySet = selector.keys();
Set<SelectionKey> keySet2 = selector.keys();
assertSame(keySet, keySet2);
assertEquals(1,keySet.size());
SelectionKey key2 = keySet.iterator().next();
assertEquals(key,key2);
// Any attempt to modify keys will cause UnsupportedOperationException
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
SelectionKey key3 = sc.register(selector, SelectionKey.OP_READ);
try {
keySet2.add(key3);
fail("should throw UnsupportedOperationException");
} catch (UnsupportedOperationException e) {
// expected
}
try {
keySet2.remove(key3);
fail("should throw UnsupportedOperationException");
} catch (UnsupportedOperationException e) {
// expected
}
try {
keySet2.clear();
fail("should throw UnsupportedOperationException");
} catch (UnsupportedOperationException e) {
// expected
}
selector.close();
try {
selector.keys();
fail("should throw ClosedSelectorException");
} catch (ClosedSelectorException e) {
// expected
}
}
/**
* @tests java.nio.channels.Selector#keys()
*/
@TestTargetNew(
level = TestLevel.COMPLETE,
notes = "",
method = "selectedKeys",
args = {}
)
public void test_selectedKeys() throws IOException {
SocketChannel sc = SocketChannel.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
try {
int count = 0;
sc.connect(LOCAL_ADDRESS);
count = blockingSelect(SelectType.NULL, 0);
assertEquals(1, count);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Set<SelectionKey> selectedKeys2 = selector.selectedKeys();
assertSame(selectedKeys, selectedKeys2);
assertEquals(1, selectedKeys.size());
assertEquals(ssc.keyFor(selector), selectedKeys.iterator().next());
// add one key into selectedKeys
try {
selectedKeys.add(ssc.keyFor(selector));
fail("Should throw UnsupportedOperationException");
} catch (UnsupportedOperationException e) {
// expected
}
// no exception should be thrown
selectedKeys.clear();
Set<SelectionKey> selectedKeys3 = selector.selectedKeys();
assertSame(selectedKeys, selectedKeys3);
ssc.keyFor(selector).cancel();
assertEquals(0, selectedKeys.size());
selector.close();
try {
selector.selectedKeys();
fail("should throw ClosedSelectorException");
} catch (ClosedSelectorException e) {
// expected
}
} finally {
sc.close();
}
}
/**
* @tests java.nio.channel.Selector#selectNow()
*/
@TestTargetNew(
level = TestLevel.PARTIAL_COMPLETE,
notes = "Verifies selectNow() method for Selector registered with SelectionKeys.OP_ACCEPT, SelectionKeys.OP_CONNECT, SelectionKeys.OP_READ, SelectionKeys.OP_WRITE keys.",
method = "selectNow",
args = {}
)
public void test_selectNow() throws IOException {
assert_select_OP_ACCEPT(SelectType.NOW, 0);
assert_select_OP_CONNECT(SelectType.NOW, 0);
assert_select_OP_READ(SelectType.NOW, 0);
assert_select_OP_WRITE(SelectType.NOW, 0);
}
/**
* @tests java.nio.channel.Selector#selectNow()
*/
@TestTargetNew(
level = TestLevel.PARTIAL_COMPLETE,
notes = "Verifies that ClosedSelectorException is thrown if selectNow() method is called for closed Selector.",
method = "selectNow",
args = {}
)
public void test_selectNow_SelectorClosed() throws IOException {
assert_select_SelectorClosed(SelectType.NOW, 0);
}
/**
* @test java.nio.channels.Selector#selectNow()
*/
@TestTargetNew(
level = TestLevel.PARTIAL_COMPLETE,
notes = "Verifies that selectNow() method doesn't block.",
method = "selectNow",
args = {}
)
public void test_selectNow_Timeout() throws IOException {
// make sure selectNow doesn't block
selector.selectNow();
}
/**
* @tests java.nio.channel.Selector#select()
*/
@TestTargetNew(
level = TestLevel.PARTIAL_COMPLETE,
notes = "Verifies select() method for Selector registered with SelectionKeys.OP_ACCEPT, SelectionKeys.OP_CONNECT, SelectionKeys.OP_READ, SelectionKeys.OP_WRITE keys.",
method = "select",
args = {}
)
public void test_select() throws IOException {
assert_select_OP_ACCEPT(SelectType.NULL, 0);
assert_select_OP_CONNECT(SelectType.NULL, 0);
assert_select_OP_READ(SelectType.NULL, 0);
assert_select_OP_WRITE(SelectType.NULL, 0);
}
/**
* @tests java.nio.channel.Selector#select()
*/
@TestTargetNew(
level = TestLevel.PARTIAL_COMPLETE,
notes = "Verifies that ClosedSelectorException is thrown if select() method is called for closed Selector.",
method = "select",
args = {}
)
public void test_select_SelectorClosed() throws IOException {
assert_select_SelectorClosed(SelectType.NULL, 0);
}
/**
* @tests java.nio.channel.Selector#select(long)
*/
@TestTargetNew(
level = TestLevel.PARTIAL_COMPLETE,
notes = "Verifies select(long) method for Selector registered with SelectionKeys.OP_ACCEPT, SelectionKeys.OP_CONNECT, SelectionKeys.OP_READ, SelectionKeys.OP_WRITE keys and different timeout's values.",
method = "select",
args = {long.class}
)
public void test_selectJ() throws IOException {
assert_select_OP_ACCEPT(SelectType.TIMEOUT, 0);
assert_select_OP_CONNECT(SelectType.TIMEOUT, 0);
assert_select_OP_READ(SelectType.TIMEOUT, 0);
assert_select_OP_WRITE(SelectType.TIMEOUT, 0);
assert_select_OP_ACCEPT(SelectType.TIMEOUT, WAIT_TIME);
assert_select_OP_CONNECT(SelectType.TIMEOUT, WAIT_TIME);
assert_select_OP_READ(SelectType.TIMEOUT, WAIT_TIME);
assert_select_OP_WRITE(SelectType.TIMEOUT, WAIT_TIME);
}
/**
* @tests java.nio.channel.Selector#select(long)
*/
@TestTargetNew(
level = TestLevel.PARTIAL_COMPLETE,
notes = "Verifies that ClosedSelectorException is thrown if select(long) method is called for closed Selector.",
method = "select",
args = {long.class}
)
public void test_selectJ_SelectorClosed() throws IOException {
assert_select_SelectorClosed(SelectType.TIMEOUT, 0);
selector = Selector.open();
assert_select_SelectorClosed(SelectType.TIMEOUT, WAIT_TIME);
}
/**
* @tests java.nio.channel.Selector#select(long)
*/
@TestTargetNew(
level = TestLevel.PARTIAL_COMPLETE,
notes = "Verifies IllegalArgumentException.",
method = "select",
args = {long.class}
)
public void test_selectJ_Exception() throws IOException {
try {
selector.select(-1);
} catch (IllegalArgumentException e) {
// expected
}
}
/**
* @test java.nio.channels.Selector#select(long)
*/
@TestTargetNew(
level = TestLevel.PARTIAL_COMPLETE,
notes = "Verifies that select(timeout) doesn't block.",
method = "select",
args = {long.class}
)
public void test_selectJ_Timeout() throws IOException {
// make sure select(timeout) doesn't block
selector.select(WAIT_TIME);
}
/**
* @test java.nio.channels.Selector#select(long)
*/
@TestTargetNew(
level = TestLevel.PARTIAL_COMPLETE,
notes = "Verifies select(long) method for empty selection keys.",
method = "select",
args = {long.class}
)
public void test_selectJ_Empty_Keys() throws IOException {
// regression test, see HARMONY-3888.
// make sure select(long) does wait for specified amount of
// time if keys.size() == 0 (initial state of selector).
final long SELECT_TIMEOUT = 2000;
long time1 = System.currentTimeMillis();
selector.select(SELECT_TIMEOUT);
long time2 = System.currentTimeMillis();
assertEquals("elapsed time", SELECT_TIMEOUT, (time2 - time1),
SELECT_TIMEOUT * 0.05); // 5% accuracy
}
/**
* @tests java.nio.channels.Selector#wakeup()
*/
@TestTargetNew(
level = TestLevel.COMPLETE,
notes = "",
method = "wakeup",
args = {}
)
public void test_wakeup() throws IOException {
/*
* make sure the test does not block on select
*/
selector.wakeup();
selectOnce(SelectType.NULL, 0);
selector.wakeup();
selectOnce(SelectType.TIMEOUT, 0);
// try to wakeup select. The invocation sequence of wakeup and select
// doesn't affect test result.
new Thread() {
public void run() {
try {
Thread.sleep(WAIT_TIME);
} catch (InterruptedException e) {
// ignore
}
selector.wakeup();
}
}.start();
selectOnce(SelectType.NULL, 0);
// try to wakeup select. The invocation sequence of wakeup and select
// doesn't affect test result.
new Thread() {
public void run() {
try {
Thread.sleep(WAIT_TIME);
} catch (InterruptedException e) {
// ignore
}
selector.wakeup();
}
}.start();
selectOnce(SelectType.TIMEOUT, 0);
}
public void test_keySetViewsModifications() throws IOException {
Set<SelectionKey> keys = selector.keys();
SelectionKey key1 = ssc.register(selector, SelectionKey.OP_ACCEPT);
assertTrue(keys.contains(key1));
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
SelectionKey key2 = sc.register(selector, SelectionKey.OP_READ);
assertTrue(keys.contains(key1));
assertTrue(keys.contains(key2));
key1.cancel();
assertTrue(keys.contains(key1));
selector.selectNow();
assertFalse(keys.contains(key1));
assertTrue(keys.contains(key2));
}
/**
* This test cancels a key while selecting to verify that the cancelled
* key set is processed both before and after the call to the underlying
* operating system.
*/
public void test_cancelledKeys() throws Exception {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final AtomicBoolean complete = new AtomicBoolean();
final Pipe pipe = Pipe.open();
pipe.source().configureBlocking(false);
final SelectionKey key = pipe.source().register(selector, SelectionKey.OP_READ);
Thread thread = new Thread() {
public void run() {
try {
// make sure to call key.cancel() while the main thread is selecting
Thread.sleep(500);
key.cancel();
assertFalse(key.isValid());
pipe.sink().write(ByteBuffer.allocate(4)); // unblock select()
} catch (Throwable e) {
failure.set(e);
} finally {
complete.set(true);
}
}
};
assertTrue(key.isValid());
thread.start();
do {
assertEquals(0, selector.select(5000)); // blocks
assertEquals(0, selector.selectedKeys().size());
} while (!complete.get()); // avoid spurious interrupts
assertFalse(key.isValid());
thread.join();
assertNull(failure.get());
}
private void assert_select_SelectorClosed(SelectType type, int timeout)
throws IOException {
// selector is closed
selector.close();
try {
selectOnce(type, timeout);
fail("should throw ClosedSelectorException");
} catch (ClosedSelectorException e) {
// expected
}
}
private void assert_select_OP_ACCEPT(SelectType type, int timeout)
throws IOException, ClosedChannelException {
SocketChannel sc = SocketChannel.open();
SocketChannel client = null;
try {
ssc.register(selector, SelectionKey.OP_ACCEPT);
sc.connect(LOCAL_ADDRESS);
int count = blockingSelect(type, timeout);
assertEquals(1, count);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
assertEquals(1, selectedKeys.size());
SelectionKey key = selectedKeys.iterator().next();
assertEquals(ssc.keyFor(selector), key);
assertEquals(SelectionKey.OP_ACCEPT, key.readyOps());
// select again, it should return 0
count = selectOnce(type, timeout);
assertEquals(0,count);
// but selectedKeys remains the same as previous
assertSame(selectedKeys, selector.selectedKeys());
client = ssc.accept();
selectedKeys.clear();
} finally {
try {
sc.close();
} catch (IOException e) {
// do nothing
}
if (null != client) {
client.close();
}
}
ssc.keyFor(selector).cancel();
}
private void assert_select_OP_CONNECT(SelectType type, int timeout)
throws IOException, ClosedChannelException {
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_CONNECT);
try {
sc.connect(LOCAL_ADDRESS);
int count = blockingSelect(type, timeout);
assertEquals(1, count);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
assertEquals(1, selectedKeys.size());
SelectionKey key = selectedKeys.iterator().next();
assertEquals(sc.keyFor(selector), key);
assertEquals(SelectionKey.OP_CONNECT, key.readyOps());
// select again, it should return 0
count = selectOnce(type, timeout);
assertEquals(0, count);
// but selectedKeys remains the same as previous
assertSame(selectedKeys, selector.selectedKeys());
sc.finishConnect();
selectedKeys.clear();
} finally {
try {
ssc.accept().close();
} catch (Exception e) {
// do nothing
}
try {
sc.close();
} catch (IOException e) {
// do nothing
}
}
}
private void assert_select_OP_READ(SelectType type, int timeout)
throws IOException {
SocketChannel sc = SocketChannel.open();
SocketChannel client = null;
SocketChannel sc2 = SocketChannel.open();
SocketChannel client2 = null;
try {
ssc.configureBlocking(true);
sc.connect(LOCAL_ADDRESS);
client = ssc.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
client.configureBlocking(true);
sc2.connect(LOCAL_ADDRESS);
client2 = ssc.accept();
sc2.configureBlocking(false);
sc2.register(selector, SelectionKey.OP_READ);
client2.configureBlocking(true);
client.write(ByteBuffer.wrap("a".getBytes()));
int count = blockingSelect(type, timeout);
assertEquals(1, count);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
assertEquals(1, selectedKeys.size());
SelectionKey key = selectedKeys.iterator().next();
assertEquals(sc.keyFor(selector), key);
assertEquals(SelectionKey.OP_READ, key.readyOps());
// select again, it should return 0
count = selectOnce(type, timeout);
assertEquals(0, count);
// but selectedKeys remains the same as previous
assertSame(selectedKeys, selector.selectedKeys());
sc.read(ByteBuffer.allocate(8));
// the second SocketChannel should be selected this time
client2.write(ByteBuffer.wrap("a".getBytes()));
count = blockingSelect(type, timeout);
assertEquals(1, count);
// selectedKeys still includes the key of sc, because the key of sc
// is not removed last time.
selectedKeys = selector.selectedKeys();
assertEquals(2, selectedKeys.size());
} finally {
if (null != client) {
try {
client.close();
} catch (Exception e) {
// ignore
}
}
if (null != client2) {
try {
client2.close();
} catch (Exception e) {
// ignore
}
}
try {
sc.close();
} catch (Exception e) {
// ignore
}
try {
sc2.close();
} catch (Exception e) {
// ignore
}
ssc.configureBlocking(false);
}
}
private void assert_select_OP_WRITE(SelectType type, int timeout)
throws IOException {
SocketChannel sc = SocketChannel.open();
SocketChannel client = null;
try {
sc.connect(LOCAL_ADDRESS);
ssc.configureBlocking(true);
client = ssc.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_WRITE);
int count = blockingSelect(type, timeout);
assertEquals(1, count);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
assertEquals(1, selectedKeys.size());
SelectionKey key = selectedKeys.iterator().next();
assertEquals(sc.keyFor(selector), key);
assertEquals(SelectionKey.OP_WRITE, key.readyOps());
// select again, it should return 0
count = selectOnce(type, timeout);
assertEquals(0, count);
// but selectedKeys remains the same as previous
assertSame(selectedKeys, selector.selectedKeys());
} finally {
if (null != client) {
client.close();
}
try {
sc.close();
} catch (IOException e) {
// do nothing
}
ssc.configureBlocking(false);
}
}
private int blockingSelect(SelectType type, int timeout) throws IOException {
int ret = 0;
do {
ret = selectOnce(type, timeout);
if (ret > 0) {
return ret;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// ignore
}
} while (true);
}
private int selectOnce(SelectType type, int timeout) throws IOException {
int ret = 0;
switch (type) {
case NULL:
ret = selector.select();
break;
case TIMEOUT:
ret = selector.select(timeout);
break;
case NOW:
ret = selector.selectNow();
break;
}
return ret;
}
}