/* * Copyright 2009-2010 Brian S O'Neill * * Licensed 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.cojen.dirmi; import java.io.IOException; import org.junit.*; import static org.junit.Assert.*; import org.cojen.dirmi.io.Channel; import org.cojen.dirmi.io.ChannelBroker; import org.cojen.dirmi.io.CountingBroker; import org.cojen.dirmi.io.IOExecutor; import org.cojen.dirmi.io.PipedChannelBroker; /** * * * @author Brian S O'Neill */ public class TestAsyncMethods extends AbstractTestSuite { public static void main(String[] args) { org.junit.runner.JUnitCore.main(TestAsyncMethods.class.getName()); } protected SessionStrategy createSessionStrategy(Environment env) throws Exception { return new PipedSessionStrategy(env, null, new RemoteAsyncServer()) { @Override protected ChannelBroker[] createBrokers(IOExecutor executor) throws IOException { // Use a larger buffer for testing eventual methods. ChannelBroker[] brokers = PipedChannelBroker.newPair(executor, 1000); return new ChannelBroker[] {new CountingBroker(brokers[0]), new CountingBroker(brokers[1])}; } }; } @Test public void runCommandNoCallback() throws Exception { RemoteAsync server = (RemoteAsync) sessionStrategy.remoteServer; assertFalse(server instanceof RemoteAsyncServer); server.runCommand(1); long start = System.nanoTime(); server.runCommand(10000); long end = System.nanoTime(); assertTrue((end - start) < 100L * 1000000); } @Test public void runCommandWithCallback() throws Exception { RemoteAsync server = (RemoteAsync) sessionStrategy.remoteServer; server.runCommand2(1, new CallbackImpl()); CallbackImpl callback = new CallbackImpl(); long start = System.nanoTime(); server.runCommand2(2000, callback); long end = System.nanoTime(); assertTrue((end - start) < 100L * 1000000); while (callback.value == 0) { sleep(100); } end = System.nanoTime(); assertTrue((end - start) > 2000L * 1000000); assertEquals(2000, callback.value); } @Test public void runCommandEventual() throws Exception { RemoteAsync server = (RemoteAsync) sessionStrategy.remoteServer; // Execute command that flushes immediately just to ensure callback // interface is transported. Otherwise first use might put too much // into the output buffer. server.runCommand2(1, new CallbackImpl()); // Sleep to allow ClassDescriptorCache to finish exchanges. Otherwise, // it might cause the channel with the eventual method to be flushed // and ruin the test. sleep(1000); CallbackImpl callback = new CallbackImpl(); server.runCommand3(1, callback); sleep(1000); assertEquals(0, callback.value); CallbackImpl callback2 = new CallbackImpl(); server.runCommand3(2, callback2); sleep(1000); assertEquals(0, callback.value); assertEquals(0, callback2.value); // Can only guarantee flush by flushing session. sessionStrategy.localSession.flush(); sleep(1000); assertEquals(1, callback.value); assertEquals(2, callback2.value); // Also test auto-flush by flooding with commands. callback = new CallbackImpl(); server.runCommand3(1, callback); sleep(1000); assertEquals(0, callback.value); for (int i=0; i<2000; i++) { // Use acknowledged methods to prevent thread growth. server.ack(); } sleep(1000); assertEquals(1, callback.value); // Test again with asynchronous method. callback = new CallbackImpl(); server.runCommand3(1, callback); sleep(1000); assertEquals(0, callback.value); for (int i=0; i<2000; i++) { server.sync(); } sleep(1000); assertEquals(1, callback.value); // Finally, test with eventual method that floods stream. callback = new CallbackImpl(); server.runCommand3(1, callback); sleep(1000); if (callback.value != 0) { // Background activity might have caused flush. Try again. callback = new CallbackImpl(); server.runCommand3(1, callback); sleep(1000); } assertEquals(0, callback.value); int[] data = new int[2000]; for (int i=0; i<20; i++) { server.data(data); } sleep(1000); assertEquals(1, callback.value); } @Test public void runCommandAcknowledged() throws Exception { RemoteAsync server = (RemoteAsync) sessionStrategy.remoteServer; // Verify that command was acknowledged by checking that bytes were // read by channel. CountingBroker broker = (CountingBroker) ((PipedSessionStrategy) sessionStrategy).localBroker; // Prime pump. server.runCommand2(1, new CallbackImpl()); long startRead = broker.getBytesRead(); long startWritten = broker.getBytesWritten(); server.runCommand2(1, new CallbackImpl()); long endRead = broker.getBytesRead(); long endWritten = broker.getBytesWritten(); // TODO: Possible concurrent activity problem with class descriptors. assertEquals(startRead, endRead); assertTrue(startWritten < endWritten); // Now do the real test. Make sure bytes are read. startRead = broker.getBytesRead(); startWritten = broker.getBytesWritten(); server.runCommand4(1000, new CallbackImpl()); endRead = broker.getBytesRead(); endWritten = broker.getBytesWritten(); assertTrue(startRead < endRead); assertTrue(startWritten < endWritten); } @Test public void sessionAccess() throws Exception { RemoteAsync server = (RemoteAsync) sessionStrategy.remoteServer; server.sessionAccess(); } private static class CallbackImpl implements RemoteAsync.Callback { public volatile int value; public void done(int value) { this.value = value; } } }