/* * Copyright (C) 2012 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.crsh.shell.impl.remoting; import org.crsh.AbstractTestCase; import test.shell.base.BaseProcess; import test.shell.base.BaseProcessContext; import test.shell.base.BaseProcessFactory; import test.shell.base.BaseShell; import org.crsh.cli.impl.completion.CompletionMatch; import org.crsh.cli.impl.Delimiter; import org.crsh.cli.spi.Completion; import org.crsh.shell.ErrorKind; import org.crsh.shell.Shell; import org.crsh.shell.ShellProcess; import org.crsh.shell.ShellProcessContext; import org.crsh.shell.ShellResponse; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.Arrays; import java.util.Collections; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; public class RemoteShellTestCase extends AbstractTestCase { static class ClientProcessor extends Thread { /** . */ final ObjectInputStream in; /** . */ final ObjectOutputStream out; /** . */ final Shell shell; ClientProcessor(ObjectInputStream in, ObjectOutputStream out, Shell shell) { this.in = in; this.out = out; this.shell = shell; } @Override public void run() { ClientAutomaton client = new ClientAutomaton(out, in, shell); client.run(); } } /** . */ private ObjectInputStream serverOIS; /** . */ private ObjectOutputStream clientOOS; /** . */ private ObjectInputStream clientOIS; /** . */ private ObjectOutputStream serverOOS; @Override protected void setUp() throws Exception { PipedInputStream a = new PipedInputStream(); PipedOutputStream b = new PipedOutputStream(a); PipedInputStream c = new PipedInputStream(); PipedOutputStream d = new PipedOutputStream(c); // ObjectOutputStream clientOOS = new ObjectOutputStream(b); clientOOS.flush(); ObjectOutputStream serverOOS = new ObjectOutputStream(d); serverOOS.flush(); ObjectInputStream serverOIS = new ObjectInputStream(a); ObjectInputStream clientOIS = new ObjectInputStream(c); // this.clientOIS = clientOIS; this.clientOOS = clientOOS; this.serverOIS = serverOIS; this.serverOOS = serverOOS; } public void testSerialization() throws Exception { ServerMessage message = new ServerMessage.Completion(new CompletionMatch(Delimiter.DOUBLE_QUOTE, Completion.create("pref", "ix", true))); clientOOS.writeObject(message); clientOOS.flush(); ServerMessage after = (ServerMessage)serverOIS.readObject(); System.out.println("after = " + after); } public void testPrompt() throws Exception { ClientProcessor t = new ClientProcessor(clientOIS, clientOOS, new BaseShell() { @Override public String getPrompt() { return "foo"; } }); t.start(); // ServerAutomaton server = new ServerAutomaton(serverOOS, serverOIS); assertEquals("foo", server.getPrompt()); // t.interrupt(); assertJoin(t); } public void testWelcome() throws Exception { ClientProcessor t = new ClientProcessor(clientOIS, clientOOS, new BaseShell() { @Override public String getWelcome() { return "bar"; } }); t.start(); // ServerAutomaton server = new ServerAutomaton(serverOOS, serverOIS); assertEquals("bar", server.getWelcome()); // t.interrupt(); assertJoin(t); } public void testExecute() throws Exception { ClientProcessor t = new ClientProcessor(clientOIS, clientOOS, new BaseShell(new BaseProcessFactory() { @Override public BaseProcess create(String request) { return new BaseProcess(request) { @Override public void process(String request, ShellProcessContext processContext) throws IOException { processContext.append("juu"); processContext.end(ShellResponse.ok()); } }; } })); t.start(); // ServerAutomaton server = new ServerAutomaton(serverOOS, serverOIS); ShellProcess process = server.createProcess("hello"); BaseProcessContext context = BaseProcessContext.create(process); context.execute(); assertInstance(ShellResponse.Ok.class, context.getResponse()); assertEquals("juu", context.getOutput()); // t.interrupt(); assertJoin(t); } public void testClose() throws Exception { ClientProcessor t = new ClientProcessor(clientOIS, clientOOS, new BaseShell(new BaseProcessFactory() { @Override public BaseProcess create(String request) { return new BaseProcess(request) { @Override protected ShellResponse execute(String request) { return ShellResponse.close(); } }; } })); t.start(); // ServerAutomaton server = new ServerAutomaton(serverOOS, serverOIS); ShellProcess process = server.createProcess("hello"); BaseProcessContext context = BaseProcessContext.create(process); context.execute(); ShellResponse response = context.getResponse(); assertInstance(ShellResponse.Close.class, response); // assertJoin(t); } public void testRawClose() throws Exception { ClientProcessor t = new ClientProcessor(clientOIS, clientOOS, new BaseShell(new BaseProcessFactory() { @Override public BaseProcess create(String request) { return new BaseProcess(request) { @Override protected ShellResponse execute(String request) { return ShellResponse.close(); } }; } })); t.start(); // serverOOS.writeObject(new ClientMessage.Execute(32, 50, "")); serverOOS.flush(); ServerMessage.End message = (ServerMessage.End)serverOIS.readObject(); assertInstance(ShellResponse.Close.class, message.response); // This should fail at some point try { serverOIS.readObject(); fail(); } catch (IOException e) { // OK } // assertJoin(t); } public void testExceptionDuringRequest() throws Exception { final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<RuntimeException> ex = new AtomicReference<RuntimeException>(); ClientProcessor t = new ClientProcessor(clientOIS, clientOOS, new BaseShell(new BaseProcessFactory() { int count = 0; @Override public BaseProcess create(String request) { return new BaseProcess(request) { @Override public void process(String request, final ShellProcessContext processContext) throws IOException { if (count == 0) { count = 1; new Thread() { @Override public void run() { try { latch.await(); } catch (InterruptedException e) { } processContext.end(ShellResponse.ok()); } }.start(); ex.set(new RuntimeException("this is a runtime exception")); throw ex.get(); } else { processContext.end(ShellResponse.ok()); } } }; } })); t.start(); // serverOOS.writeObject(new ClientMessage.Execute(32, 50, "")); serverOOS.flush(); // ServerMessage.End message = (ServerMessage.End)serverOIS.readObject(); ShellResponse.Error error = assertInstance(ShellResponse.Error.class, message.response); assertEquals(ErrorKind.INTERNAL, error.getKind()); assertInstance(Exception.class, error.getThrowable()); assertEquals("this is a runtime exception", error.getThrowable().getMessage()); assertEquals(Arrays.asList(ex.get().getStackTrace()), Arrays.asList(error.getThrowable().getStackTrace())); // latch.countDown(); // serverOOS.writeObject(new ClientMessage.Execute(32, 50, "")); serverOOS.flush(); // message = (ServerMessage.End)serverOIS.readObject(); assertInstance(ShellResponse.Ok.class, message.response); // t.interrupt(); assertJoin(t); } public void testCancel() throws Exception { final AtomicBoolean waiting = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); // ClientProcessor t = new ClientProcessor(clientOIS, clientOOS, new BaseShell(new BaseProcessFactory() { @Override public BaseProcess create(String request) { return new BaseProcess(request) { @Override public void process(String request, final ShellProcessContext processContext) throws IOException { new Thread() { @Override public void run() { synchronized (waiting) { if (waiting.get()) { waiting.notifyAll(); } else { waiting.set(true); } try { waiting.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { processContext.append("juu"); processContext.end(ShellResponse.ok()); } catch (IOException e) { e.printStackTrace(); } latch.countDown(); } }.start(); } @Override public void cancel() { synchronized (waiting) { waiting.notifyAll(); } } }; } })); t.start(); // ServerAutomaton server = new ServerAutomaton(serverOOS, serverOIS); ShellProcess process = server.createProcess("hello"); final BaseProcessContext context = BaseProcessContext.create(process); // final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); Thread u = new Thread() { @Override public void run() { context.execute(); ShellResponse response = context.getResponse(); assertInstance(ShellResponse.Cancelled.class, response); } }; u.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { error.set(e); } }); u.start(); // synchronized (waiting) { if (!waiting.get()) { waiting.set(true); waiting.wait(); } } // process.cancel(); // latch.await(); // t.interrupt(); assertJoin(t); assertJoin(u); if (error.get() != null) { throw failure(error.get()); } } public void testComplete() { ClientProcessor t = new ClientProcessor(clientOIS, clientOOS, new BaseShell() { @Override public CompletionMatch complete(String prefix) { return new CompletionMatch(Delimiter.DOUBLE_QUOTE, Completion.create(prefix, "ix", true)); } }); t.start(); // ServerAutomaton server = new ServerAutomaton(serverOOS, serverOIS); CompletionMatch completion = server.complete("pref"); assertEquals(Delimiter.DOUBLE_QUOTE, completion.getDelimiter()); Completion value = completion.getValue(); assertEquals("pref", value.getPrefix()); assertEquals(1, value.getSize()); assertEquals(Collections.singleton("ix"), value.getValues()); assertEquals(Boolean.TRUE, value.get("ix")); // t.interrupt(); assertJoin(t); } }