/*
* Copyright 2015 Red Hat, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*
*
* Copyright (c) 2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*
*/
package io.vertx.ext.shell;
import io.vertx.core.Context;
import io.vertx.core.Vertx;
import io.vertx.ext.shell.command.CommandBuilder;
import io.vertx.ext.shell.support.TestCommands;
import io.vertx.ext.shell.support.TestTermServer;
import io.vertx.ext.shell.support.TestTtyConnection;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
/**
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
@RunWith(VertxUnitRunner.class)
public class ShellCloseTest {
Vertx vertx;
TestCommands registry;
TestTermServer termServer;
ShellServer shellServer;
@Before
public void before(TestContext context) {
vertx = Vertx.vertx();
registry = new TestCommands(vertx);
termServer = new TestTermServer(vertx);
}
@After
public void after(TestContext context) {
vertx.close(context.asyncAssertSuccess());
shellServer = null;
}
private void startShellServer(TestContext context, long sessionTimeout, long reaperInterval) {
if (shellServer != null) {
throw new IllegalStateException("Already started");
}
Async latch = context.async();
shellServer = ShellServer.create(vertx, new ShellServerOptions().setSessionTimeout(sessionTimeout).setReaperInterval(reaperInterval)).
registerTermServer(termServer).
registerCommandResolver(registry).
listen(context.asyncAssertSuccess(v -> latch.complete()));
latch.awaitSuccess(2000);
}
@Test
public void testSessionExpires(TestContext context) throws Exception {
Async ended = context.async();
registry.add(CommandBuilder.command("cmd").processHandler(process -> {
process.endHandler(v -> {
ended.complete();
});
}));
startShellServer(context, 100, 100);
long now = System.currentTimeMillis();
TestTtyConnection conn = termServer.openConnection();
conn.read("cmd\r");
ended.awaitSuccess(2000);
context.assertTrue(conn.isClosed());
long elapsed = System.currentTimeMillis() - now;
context.assertTrue(elapsed < 1000);
}
@Test
public void testLastAccessed(TestContext context) throws Exception {
startShellServer(context, 100, 100);
TestTtyConnection conn = termServer.openConnection();
for (int i = 0; i < 100; i++) {
conn.read("" + i);
Thread.sleep(10);
context.assertFalse(conn.isClosed());
}
context.assertTrue(conn.getCloseLatch().await(2, TimeUnit.SECONDS));
}
@Test
public void testCloseShellServer(TestContext context) throws Exception {
testClose(context, conn -> {
Async async = context.async();
shellServer.close(context.asyncAssertSuccess(v -> async.complete()));
async.awaitSuccess(2000);
});
}
@Test
public void testCloseConnection(TestContext context) throws Exception {
testClose(context, (conn) -> {
conn.close();
});
shellServer.close();
shellServer = null;
}
public void testClose(TestContext context, Consumer<TestTtyConnection> closer) throws Exception {
Async processEnded = context.async();
Async processStarted = context.async();
registry.add(CommandBuilder.command("cmd").processHandler(process -> {
process.endHandler(v -> {
processEnded.complete();
});
processStarted.complete();
}));
startShellServer(context, 30000, 100);
TestTtyConnection conn = termServer.openConnection();
conn.read("cmd\r");
processStarted.awaitSuccess(2000);
closer.accept(conn);
processEnded.awaitSuccess(2000);
context.assertTrue(conn.getCloseLatch().await(2, TimeUnit.SECONDS));
}
@Test
public void testCloseWhileEnding(TestContext context) throws Exception {
Async processStarted = context.async();
Async processEnding = context.async();
Async processEnd = context.async();
Async closed = context.async();
AtomicReference<Runnable> end = new AtomicReference<>();
registry.add(CommandBuilder.command("cmd").processHandler(process -> {
process.endHandler(v -> {
processEnding.complete();
processEnd.awaitSuccess(2000);
});
Context ctx = process.vertx().getOrCreateContext();
end.set(() -> {
ctx.runOnContext(v -> {
process.end();
});
});
processStarted.complete();
}));
startShellServer(context, 30000, 100);
TestTtyConnection conn = termServer.openConnection();
conn.read("cmd\r");
processStarted.awaitSuccess(2000);
end.get().run();
processEnding.awaitSuccess(2000);
shellServer.close(context.asyncAssertSuccess(v -> {
closed.complete();
}
));
processEnd.complete();
}
}