aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorAdam Domurad <[email protected]>2013-04-25 17:05:31 -0400
committerAdam Domurad <[email protected]>2013-04-25 17:05:31 -0400
commit6d5754626525233d2bda2e388632f8b4476484c9 (patch)
tree51f9d21ddf410be3415509ddfd2a7a6f0941b9de /tests
parent61ca0a975341f40fbbb46379b10fbf77f0bf3d95 (diff)
Add accidentally not included files
Diffstat (limited to 'tests')
-rw-r--r--tests/netx/unit/sun/applet/PluginAppletViewerTest.java249
-rw-r--r--tests/test-extensions/net/sourceforge/jnlp/AsyncCall.java102
-rw-r--r--tests/test-extensions/sun/applet/mock/PluginPipeMock.java121
3 files changed, 472 insertions, 0 deletions
diff --git a/tests/netx/unit/sun/applet/PluginAppletViewerTest.java b/tests/netx/unit/sun/applet/PluginAppletViewerTest.java
new file mode 100644
index 0000000..aa15b47
--- /dev/null
+++ b/tests/netx/unit/sun/applet/PluginAppletViewerTest.java
@@ -0,0 +1,249 @@
+package sun.applet;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.Callable;
+
+import net.sourceforge.jnlp.AsyncCall;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import sun.applet.mock.PluginPipeMock;
+
+public class PluginAppletViewerTest {
+
+ /**************************************************************************
+ * Test setup *
+ **************************************************************************/
+
+ ThreadGroup spawnedForTestThreadGroup; // Set up before each test
+ PluginPipeMock pipeMock; // Set up before each test
+
+ /* By providing custom implementations of the input stream & output stream used by PluginStreamHandler,
+ * we are able to mock the C++-side of the plugin. We do this by sending the messages the Java-side expects
+ * to receive. Additionally, we able to test that the Java-side sends the correct requests.
+ * See PluginPipeMock for more details.
+ */
+ private void installPipeMock() {
+ AppletSecurityContextManager.addContext(0, new PluginAppletSecurityContext(0, false /* no security */));
+
+ pipeMock = new PluginPipeMock();
+
+ PluginStreamHandler streamHandler = new PluginStreamHandler(pipeMock.getResponseInputStream(), pipeMock.getRequestOutputStream());
+ PluginAppletViewer.setStreamhandler(streamHandler);
+ PluginAppletViewer.setPluginCallRequestFactory(new PluginCallRequestFactory());
+
+ streamHandler.startProcessing();
+ }
+
+ /* Call installPipeMock, wrapping the threads it creates in a ThreadGroup.
+ * This allows us to stop the message handling threads we spawn, while normally
+ * this would be difficult as they are meant to be alive at all times.
+ */
+ @Before
+ public void setupMockedMessageHandling() throws Exception {
+ spawnedForTestThreadGroup = new ThreadGroup("PluginAppletViewerTestThreadGroup") {
+ public void uncaughtException(Thread t, Throwable e) {
+ // Silent death for plugin message handler threads
+ }
+ };
+ // Do set-up in a thread so we can pass along our thread-group, used for clean-up.
+ Thread initThread = new Thread(spawnedForTestThreadGroup, "InstallPipeMockThread") {
+ @Override
+ public void run() {
+ installPipeMock();
+ }
+ };
+ initThread.start();
+ initThread.join();
+ }
+
+ @After
+ @SuppressWarnings("deprecation") // 'stop' must be used, 'interrupt' is too gentle.
+ public void cleanUpMessageHandlingThreads() throws Exception {
+ spawnedForTestThreadGroup.stop();
+ }
+
+ /**************************************************************************
+ * Test cases *
+ * A PluginStreamHandler is installed for each, see 'installPipeMock'. *
+ **************************************************************************/
+
+ @Test
+ public void testJavascriptCall() throws Exception {
+ /* JS call parameters */
+ final int jsObjectID = 0;
+ final String callName = "testfunction";
+ final Object[] arguments = { "testargument", 1 }; // Arbitrary objects
+
+ AsyncCall<Object> call = AsyncCall.startWithTimeOut(new Callable<Object>() {
+ public Object call() {
+ return PluginAppletViewer.call(jsObjectID, callName, arguments);
+ }
+ });
+
+ String message = pipeMock.getNextRequest();
+ Object expectedReturn = new Object();
+ pipeMock.sendResponse("context 0 reference "
+ + parseAndCheckJSCall(message, jsObjectID, callName, arguments)
+ + " JavaScriptCall " + storeObject(expectedReturn));
+
+ assertEquals(expectedReturn, call.join());
+ }
+
+ @Test
+ public void testJavascriptEval() throws Exception {
+ /* JS eval parameters */
+ final int jsObjectID = 0;
+ final String callName = "testfunction";
+
+ AsyncCall<Object> call = AsyncCall.startWithTimeOut(new Callable<Object>() {
+ public Object call() {
+ return PluginAppletViewer.eval(jsObjectID, callName);
+ }
+ });
+
+ String message = pipeMock.getNextRequest();
+ Object expectedReturn = new Object();
+ pipeMock.sendResponse("context 0 reference "
+ + parseAndCheckJSEval(message, jsObjectID, callName)
+ + " JavaScriptEval " + storeObject(expectedReturn));
+
+ assertEquals(expectedReturn, call.join());
+ }
+
+ @Test
+ public void testJavascriptFinalize() throws Exception {
+ final int jsObjectID = 0;
+ AsyncCall<Void> call = AsyncCall.startWithTimeOut(new Callable<Void>() {
+ public Void call() {
+ PluginAppletViewer.JavaScriptFinalize(jsObjectID);
+ return null;
+ }
+ });
+
+ String message = pipeMock.getNextRequest();
+ pipeMock.sendResponse("context 0 reference "
+ + parseAndCheckJSFinalize(message, jsObjectID)
+ + " JavaScriptFinalize ");
+
+ call.join();
+ }
+
+ @Test
+ public void testJavascriptToString() throws Exception {
+ final int jsObjectID = 0;
+ AsyncCall<String> call = AsyncCall.startWithTimeOut(new Callable<String>() {
+ public String call() {
+ return PluginAppletViewer.javascriptToString(jsObjectID);
+ }
+ });
+
+ String message = pipeMock.getNextRequest();
+
+ String expectedReturn = "testreturn";
+ pipeMock.sendResponse("context 0 reference "
+ + parseAndCheckJSToString(message, jsObjectID)
+ + " JavaScriptToString " + storeObject(expectedReturn));
+
+ assertEquals(expectedReturn, call.join());
+ }
+
+ /**************************************************************************
+ * Test utilities *
+ **************************************************************************/
+
+ /*
+ * Helpers for manipulating the object mapping using to refer to objects in
+ * the plugin
+ */
+ private static Object getStoredObject(int id) {
+ return PluginObjectStore.getInstance().getObject(id);
+ }
+
+ private static int storeObject(Object obj) {
+ PluginObjectStore.getInstance().reference(obj);
+ return PluginObjectStore.getInstance().getIdentifier(obj);
+ }
+
+ /*
+ * Asserts that the message is a valid javascript request and returns the
+ * reference number
+ */
+ private static int parseAndCheckJSMessage(String message, int messageLength,
+ String messageType, int contextObjectID) {
+ System.out.println(message);
+ String[] parts = message.split(" ");
+ assertEquals(messageLength, parts.length);
+
+ assertEquals("instance", parts[0]);
+ assertEquals("0", parts[1]); // JSCall's are prefixed with a dummy '0' instance
+ assertEquals("reference", parts[2]);
+ int reference = Integer.parseInt(parts[3]);
+ assertEquals(messageType, parts[4]);
+
+ assertEquals(contextObjectID, Integer.parseInt(parts[5]));
+ return reference;
+ }
+
+ /*
+ * Asserts that the message is a valid javascript request and returns the
+ * reference number
+ */
+ private static int parseAndCheckJSMessage(String message,
+ String messageType, int contextObjectID, String stringArg,
+ Object[] arguments) {
+ int expectedLength = 7 + arguments.length;
+ int reference = parseAndCheckJSMessage(message, expectedLength, messageType, contextObjectID);
+
+ String[] parts = message.split(" ");
+ assertEquals(stringArg, getStoredObject(Integer.parseInt(parts[6])));
+
+ for (int i = 0; i < arguments.length; i++) {
+ int objectID = Integer.parseInt(parts[7+i]);
+ assertEquals(arguments[i], getStoredObject(objectID));
+ }
+
+ return reference;
+ }
+
+ /*
+ * Asserts that the message is a valid javascript method call request, and
+ * returns the reference number
+ */
+ public static int parseAndCheckJSCall(String message, int contextObjectID,
+ String callName, Object[] arguments) {
+ return parseAndCheckJSMessage(message, "Call", contextObjectID,
+ callName, arguments);
+ }
+
+ /*
+ * Asserts that the message is a valid javascript Eval request, and returns
+ * the reference number
+ */
+ public static int parseAndCheckJSEval(String message, int contextObjectID,
+ String evalString) {
+ return parseAndCheckJSMessage(message, "Eval", contextObjectID,
+ evalString, new Object[] {});
+ }
+
+ /*
+ * Asserts that the message is a valid javascript Finalize request, and returns
+ * the reference number
+ */
+ public static int parseAndCheckJSFinalize(String message, int contextObjectID) {
+ int expectedLength = 6;
+ return parseAndCheckJSMessage(message, expectedLength, "Finalize", contextObjectID);
+ }
+
+ /*
+ * Asserts that the message is a valid javascript ToString request, and returns
+ * the reference number
+ */
+ public static int parseAndCheckJSToString(String message, int contextObjectID) {
+ int expectedLength = 6;
+ return parseAndCheckJSMessage(message, expectedLength, "ToString", contextObjectID);
+ }
+} \ No newline at end of file
diff --git a/tests/test-extensions/net/sourceforge/jnlp/AsyncCall.java b/tests/test-extensions/net/sourceforge/jnlp/AsyncCall.java
new file mode 100644
index 0000000..f3f1716
--- /dev/null
+++ b/tests/test-extensions/net/sourceforge/jnlp/AsyncCall.java
@@ -0,0 +1,102 @@
+package net.sourceforge.jnlp;
+
+import java.util.concurrent.Callable;
+
+/**
+ * A call that runs on a separate thread, with an optional timeout. It takes a runnable and allows
+ * joining.
+ *
+ * On join, throws any exceptions that occurred within the call, or a TimeOutException if
+ * it did not finish. Returns the value from the call.
+ */
+public class AsyncCall<T> {
+ static public class TimeOutException extends RuntimeException {
+ public TimeOutException() {
+ super("Call did not finish within the allocated time.");
+ }
+ }
+
+ private Thread handler;
+ private Callable<T> callable;
+ private long timeout;
+ private T callResult;
+
+ /* Captures exception from async call */
+ private Exception asyncException = null;
+
+ /* Create an AsyncCall with a given time-out */
+ public AsyncCall(Callable<T> callable, long timeout) {
+ this.callable = callable;
+ this.timeout = timeout;
+ this.handler = new HandlerThread();
+ }
+
+ /* Create an AsyncCall with (effectively) no time-out */
+ public AsyncCall(Callable<T> call) {
+ this(call, Long.MAX_VALUE);
+ }
+
+ /* Chains construction + start for convenience */
+ public static <T> AsyncCall<T> startWithTimeOut(Callable<T> callable, long timeout) {
+ AsyncCall<T> asyncCall = new AsyncCall<T>(callable, timeout);
+ asyncCall.start();
+ return asyncCall;
+ }
+
+ /* Chains construction + start for convenience */
+ public static <T> AsyncCall<T> startWithTimeOut(Callable<T> callable) {
+ return startWithTimeOut(callable, 1000); // Default timeout of 1 second
+ }
+
+ public void start() {
+ this.handler.start();
+ }
+
+ // Rethrows exceptions from handler thread, and throws TimeOutException in case of time-out.
+ public T join() throws Exception {
+ handler.join();
+ if (asyncException != null) {
+ throw asyncException;
+ }
+ return callResult;
+ }
+
+ /* The handler thread is responsible for timing-out the Callable thread.
+ * The resulting thread */
+ private class HandlerThread extends Thread {
+ @Override
+ public void run() {
+ Thread thread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ /* Capture result of the call */
+ callResult = callable.call();
+ } catch (Exception e) {
+ /* In case of exception, capture for re-throw */
+ asyncException = e;
+ }
+ handler.interrupt(); // Finish early
+ }
+ };
+
+ thread.start();
+
+ try {
+ Thread.sleep(timeout);
+ } catch (InterruptedException e) {
+ // Finish early
+ return;
+ }
+
+ if (thread.isAlive()) {
+ asyncException = new TimeOutException();
+ }
+
+ // Make sure the thread is finished
+ while (thread.isAlive()) {
+ thread.interrupt();
+ }
+ }
+ }
+}
diff --git a/tests/test-extensions/sun/applet/mock/PluginPipeMock.java b/tests/test-extensions/sun/applet/mock/PluginPipeMock.java
new file mode 100644
index 0000000..2435c66
--- /dev/null
+++ b/tests/test-extensions/sun/applet/mock/PluginPipeMock.java
@@ -0,0 +1,121 @@
+package sun.applet.mock;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringReader;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Helper for getting an input & output stream for use with PluginStreamHandler.
+ * Provides a convenient way of reading the Java requests and sending mocked
+ * plugin responses.
+ *
+ * The handling of these requests should be done on a different thread from the
+ * tested method, as icedtea-web will block waiting for a reply after sending a
+ * request.
+ */
+public class PluginPipeMock {
+ private ResponseInputPipeMock responseInputStream = new ResponseInputPipeMock();
+ private RequestOutputPipeMock requestOutputStream = new RequestOutputPipeMock();
+
+ /*
+ * A queue of mocked responses that are sent as replies to icedtea-web
+ * Java-side requests.
+ */
+ private BlockingQueue<String> mockedResponseQueue = new LinkedBlockingQueue<String>();
+
+ /*
+ * A queue of actual (ie, not mocked) requests that come from methods
+ * under test.
+ */
+ private BlockingQueue<String> requestQueue = new LinkedBlockingQueue<String>();
+
+ public InputStream getResponseInputStream() {
+ return responseInputStream;
+ }
+
+ public OutputStream getRequestOutputStream() {
+ return requestOutputStream;
+ }
+
+ public String getNextRequest() {
+ try {
+ return requestQueue.take();
+ } catch (InterruptedException e) {
+ // Nothing to do
+ return null;
+ }
+ }
+
+ public void sendResponse(String response) {
+ try {
+ mockedResponseQueue.put(response);
+ } catch (InterruptedException e) {
+ // Nothing to do
+ }
+ }
+
+ /**
+ * Queues mocked responses and sends them as replies to icedtea-web. A
+ * synchronized message queue is read from. Blocks until it gets the next
+ * message.
+ */
+ private class ResponseInputPipeMock extends InputStream {
+ private StringReader reader = null;
+
+ @Override
+ public int read() throws IOException {
+ try {
+ while (true) {
+ if (reader == null) {
+ reader = new StringReader(mockedResponseQueue.take() + '\n');
+ }
+ int chr = reader.read();
+ if (chr == -1) {
+ reader = null;
+ continue;
+ }
+ return chr;
+ }
+ } catch (InterruptedException e) {
+ // Nothing to do
+ return -1;
+ }
+ }
+
+ /* Necessary for correct behaviour with BufferedReader! */
+ @Override
+ public int read(byte b[], int off, int len) throws IOException {
+ if (len == 0) {
+ return 0;
+ }
+ b[off] = (byte) read();
+ return 1;
+ }
+ }
+
+ /**
+ * Outputs requests from icedtea-web as a stream of lines. A synchronized
+ * message queue is written to.
+ */
+ private class RequestOutputPipeMock extends OutputStream {
+ private StringBuilder lineBuffer = new StringBuilder();
+
+ @Override
+ public synchronized void write(int b) throws IOException {
+ try {
+ char chr = (char) b;
+ if (chr == '\0') {
+ requestQueue.put(lineBuffer.toString());
+ lineBuffer.setLength(0);
+ } else {
+ lineBuffer.append((char) b);
+ }
+ } catch (InterruptedException e) {
+ // Nothing to do
+ }
+ }
+ }
+}