/* * 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.term; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.ext.unit.Async; import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.VertxUnitRunner; import org.apache.commons.net.telnet.EchoOptionHandler; import org.apache.commons.net.telnet.SimpleOptionHandler; import org.apache.commons.net.telnet.TelnetClient; import org.apache.commons.net.telnet.TerminalTypeOptionHandler; import org.apache.commons.net.telnet.WindowSizeOptionHandler; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.function.Consumer; /** * @author <a href="mailto:julien@julienviet.com">Julien Viet</a> */ @RunWith(VertxUnitRunner.class) public class TelnetTermServerTest { private TelnetClient client; private Vertx vertx; private TermServer server; @Before public void before() throws Exception { vertx = Vertx.vertx(); client = new TelnetClient(); client.addOptionHandler(new EchoOptionHandler(false, false, true, true)); client.addOptionHandler(new SimpleOptionHandler(0, false, false, true, true)); client.addOptionHandler(new TerminalTypeOptionHandler("xterm-color", false, false, true, false)); } @After public void after(TestContext context) { if (client != null && client.isConnected()) { try { client.disconnect(); } catch (IOException ignore) { } } vertx.close(context.asyncAssertSuccess()); } private void startTelnet(TestContext context, Handler<Term> termHandler) { startTelnet(context, new TelnetTermOptions(), termHandler); } private void startTelnet(TestContext context, TelnetTermOptions options, Handler<Term> termHandler) { server = TermServer.createTelnetTermServer(vertx, options.setPort(4000)); server.termHandler(termHandler); Async async = context.async(); server.listen(context.asyncAssertSuccess(v -> async.complete())); async.awaitSuccess(5000); } @Test public void testRead(TestContext context) throws IOException { Async async = context.async(); startTelnet(context, term -> { term.stdinHandler(s -> { context.assertEquals("hello_from_client", s); async.complete(); }); }); client.connect("localhost", server.actualPort()); Writer writer = new OutputStreamWriter(client.getOutputStream()); writer.write("hello_from_client"); writer.flush(); } @Test public void testWrite(TestContext context) throws IOException { startTelnet(context, term -> { term.write("hello_from_server"); }); client.connect("localhost", server.actualPort()); Reader reader = new InputStreamReader(client.getInputStream()); StringBuilder sb = new StringBuilder("hello_from_server"); while (sb.length() > 0) { int c = reader.read(); context.assertNotEquals(-1, c); context.assertEquals(c, (int)sb.charAt(0)); sb.deleteCharAt(0); } } @Test public void testCloseHandler(TestContext context) throws IOException { Async async1 = context.async(); Async async2 = context.async(); startTelnet(context, term -> { term.closeHandler(v -> { async2.complete(); }); async1.complete(); }); client.connect("localhost", server.actualPort()); async1.awaitSuccess(4000); client.disconnect(); } @Test public void testClose(TestContext context) throws Exception { testClose(context, term -> { vertx.setTimer(10, id -> { term.close(); }); }); } @Test public void testCloseImmediatly(TestContext context) throws Exception { testClose(context, Term::close); } private void testClose(TestContext context, Consumer<Term> closer) throws Exception { Async async1 = context.async(); Async async2 = context.async(); startTelnet(context, term -> { term.closeHandler(v -> { async2.complete(); }); closer.accept(term); async1.complete(); }); client.connect("localhost", server.actualPort()); async1.await(4000); OutputStream out = client.getOutputStream(); long now = System.currentTimeMillis(); while (true) { context.assertTrue(System.currentTimeMillis() - now < 10000); try { out.write(4); out.flush(); } catch (IOException ignore) { break; } Thread.sleep(10); } } @Test public void testType(TestContext context) throws Exception { Async async = context.async(); startTelnet(context, term -> { long now = System.currentTimeMillis(); vertx.setPeriodic(10, id -> { context.assertTrue(System.currentTimeMillis() - now < 10000); if (term.type() != null) { vertx.cancelTimer(id); context.assertEquals("xterm-color", term.type()); async.complete(); } }); }); client.connect("localhost", server.actualPort()); } @Test public void testWindowSize(TestContext context) throws Exception { Async async = context.async(); startTelnet(context, term -> { context.assertEquals(-1, term.width()); context.assertEquals(-1, term.height()); term.resizehandler(v -> { context.assertEquals(10, term.width()); context.assertEquals(20, term.height()); async.complete(); }); }); client.addOptionHandler(new WindowSizeOptionHandler(10, 20, false, false, true, false)); client.connect("localhost", server.actualPort()); } @Test public void testOutBinaryTrue(TestContext context) throws Exception { startTelnet(context, new TelnetTermOptions().setOutBinary(true), term -> { term.write("\u20AC"); }); client.addOptionHandler(new WindowSizeOptionHandler(10, 20, false, false, true, false)); client.connect("localhost", server.actualPort()); InputStream in = client.getInputStream(); context.assertEquals(226, in.read()); context.assertEquals(130, in.read()); context.assertEquals(172, in.read()); } @Test public void testOutBinaryFalse(TestContext context) throws Exception { byte[] expected = StandardCharsets.US_ASCII.encode("€").array(); startTelnet(context, new TelnetTermOptions().setOutBinary(false), term -> { term.write("\u20AC"); }); client.addOptionHandler(new WindowSizeOptionHandler(10, 20, false, false, true, false)); client.connect("localhost", server.actualPort()); InputStream in = client.getInputStream(); for (int i = 0;i < expected.length;i++) { context.assertEquals((int)expected[i], in.read()); } } @Test public void testDifferentCharset(TestContext context) throws Exception { startTelnet(context, new TelnetTermOptions().setCharset("ISO_8859_1"), term -> { term.write("\u20AC"); term.close(); }); client.connect("localhost", server.actualPort()); InputStream in = client.getInputStream(); int b = in.read(); context.assertEquals(63, b); } @Test public void testKeymapFromFilesystem(TestContext context) throws Exception { URL url = TermServer.class.getResource(SSHTermOptions.DEFAULT_INPUTRC); File f = new File(url.toURI()); startTelnet(context, new TelnetTermOptions().setIntputrc(f.getAbsolutePath()), Term::close); client.connect("localhost", server.actualPort()); } }