using System;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Common;
using Renci.SshNet.Messages;
using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes
{
    /// 
    /// Provides functionality to connect and interact with SSH server.
    /// 
    [TestClass]
    public partial class SessionTest : TestBase
    {
        [TestMethod]
        public void ConnectShouldSkipLinesBeforeProtocolIdentificationString()
        {
            var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122);
            var connectionInfo = CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5));
            using (var serverStub = new AsyncSocketListener(serverEndPoint))
            {
                serverStub.Connected += (socket) =>
                {
                    socket.Send(Encoding.ASCII.GetBytes("\r\n"));
                    socket.Send(Encoding.ASCII.GetBytes("WELCOME banner\r\n"));
                    socket.Send(Encoding.ASCII.GetBytes("SSH-666-SshStub\r\n"));
                    socket.Shutdown(SocketShutdown.Send);
                };
                serverStub.Start();
                using (var session = new Session(connectionInfo))
                {
                    try
                    {
                        session.Connect();
                        Assert.Fail();
                    }
                    catch (SshConnectionException ex)
                    {
                        Assert.IsNull(ex.InnerException);
                        Assert.AreEqual("Server version '666' is not supported.", ex.Message);
                        Assert.AreEqual("SSH-666-SshStub", connectionInfo.ServerVersion);
                    }
                }
            }
        }
        [TestMethod]
        public void ConnectShouldSupportProtocolIdentificationStringThatDoesNotEndWithCrlf()
        {
            var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122);
            var connectionInfo = CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5));
            using (var serverStub = new AsyncSocketListener(serverEndPoint))
            {
                serverStub.Connected += (socket) =>
                {
                    socket.Send(Encoding.ASCII.GetBytes("\r\n"));
                    socket.Send(Encoding.ASCII.GetBytes("WELCOME banner\r\n"));
                    socket.Send(Encoding.ASCII.GetBytes("SSH-666-SshStub"));
                    socket.Shutdown(SocketShutdown.Send);
                };
                serverStub.Start();
                using (var session = new Session(connectionInfo))
                {
                    try
                    {
                        session.Connect();
                        Assert.Fail();
                    }
                    catch (SshConnectionException ex)
                    {
                        Assert.IsNull(ex.InnerException);
                        Assert.AreEqual("Server version '666' is not supported.", ex.Message);
                        Assert.AreEqual("SSH-666-SshStub", connectionInfo.ServerVersion);
                    }
                }
            }
        }
        [TestMethod]
        public void ConnectShouldThrowSshOperationExceptionWhenServerDoesNotRespondWithinConnectionTimeout()
        {
            var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122);
            var timeout = TimeSpan.FromMilliseconds(500);
            Socket clientSocket = null;
            using (var serverStub = new AsyncSocketListener(serverEndPoint))
            {
                serverStub.Connected += (socket) =>
                {
                    socket.Send(Encoding.ASCII.GetBytes("\r\n"));
                    socket.Send(Encoding.ASCII.GetBytes("WELCOME banner\r\n"));
                    clientSocket = socket;
                };
                serverStub.Start();
                using (var session = new Session(CreateConnectionInfo(serverEndPoint, TimeSpan.FromMilliseconds(500))))
                {
                    try
                    {
                        session.Connect();
                        Assert.Fail();
                    }
                    catch (SshOperationTimeoutException ex)
                    {
                        Assert.IsNull(ex.InnerException);
                        Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, "Socket read operation has timed out after {0:F0} milliseconds.", timeout.TotalMilliseconds), ex.Message);
                        Assert.IsNotNull(clientSocket);
                        Assert.IsTrue(clientSocket.Connected);
                        // shut down socket
                        clientSocket.Shutdown(SocketShutdown.Send);
                    }
                }
            }
        }
        [TestMethod]
        public void ConnectShouldSshConnectionExceptionWhenServerResponseDoesNotContainProtocolIdentificationString()
        {
            var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122);
            // response ends with CRLF
            using (var serverStub = new AsyncSocketListener(serverEndPoint))
            {
                serverStub.Connected += (socket) =>
                {
                    socket.Send(Encoding.ASCII.GetBytes("\r\n"));
                    socket.Send(Encoding.ASCII.GetBytes("WELCOME banner\r\n"));
                    socket.Shutdown(SocketShutdown.Send);
                };
                serverStub.Start();
                using (var session = new Session(CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5))))
                {
                    try
                    {
                        session.Connect();
                        Assert.Fail();
                    }
                    catch (SshConnectionException ex)
                    {
                        Assert.IsNull(ex.InnerException);
                        Assert.AreEqual("Server response does not contain SSH protocol identification.", ex.Message);
                    }
                }
            }
            // response does not end with CRLF
            using (var serverStub = new AsyncSocketListener(serverEndPoint))
            {
                serverStub.Connected += (socket) =>
                {
                    socket.Send(Encoding.ASCII.GetBytes("\r\n"));
                    socket.Send(Encoding.ASCII.GetBytes("WELCOME banner"));
                    socket.Shutdown(SocketShutdown.Send);
                };
                serverStub.Start();
                using (var session = new Session(CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5))))
                {
                    try
                    {
                        session.Connect();
                        Assert.Fail();
                    }
                    catch (SshConnectionException ex)
                    {
                        Assert.IsNull(ex.InnerException);
                        Assert.AreEqual("Server response does not contain SSH protocol identification.", ex.Message);
                    }
                }
            }
            // last line is empty
            using (var serverStub = new AsyncSocketListener(serverEndPoint))
            {
                serverStub.Connected += (socket) =>
                {
                    socket.Send(Encoding.ASCII.GetBytes("\r\n"));
                    socket.Send(Encoding.ASCII.GetBytes("WELCOME banner\r\n"));
                    socket.Send(Encoding.ASCII.GetBytes("\r\n"));
                    socket.Shutdown(SocketShutdown.Send);
                };
                serverStub.Start();
                using (var session = new Session(CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5))))
                {
                    try
                    {
                        session.Connect();
                        Assert.Fail();
                    }
                    catch (SshConnectionException ex)
                    {
                        Assert.IsNull(ex.InnerException);
                        Assert.AreEqual("Server response does not contain SSH protocol identification.", ex.Message);
                    }
                }
            }
        }
        /// 
        ///A test for SessionSemaphore
        ///
        [TestMethod()]
        [Ignore]
        public void SessionSemaphoreTest()
        {
            ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
            Session target = new Session(connectionInfo); // TODO: Initialize to an appropriate value
            SemaphoreLight actual;
            actual = target.SessionSemaphore;
            Assert.Inconclusive("Verify the correctness of this test method.");
        }
        /// 
        ///A test for IsConnected
        ///
        [TestMethod()]
        [Ignore]
        public void IsConnectedTest()
        {
            ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
            Session target = new Session(connectionInfo); // TODO: Initialize to an appropriate value
            bool actual;
            actual = target.IsConnected;
            Assert.Inconclusive("Verify the correctness of this test method.");
        }
        /// 
        ///A test for ClientInitMessage
        ///
        [TestMethod()]
        [Ignore]
        public void ClientInitMessageTest()
        {
            ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
            Session target = new Session(connectionInfo); // TODO: Initialize to an appropriate value
            Message actual;
            actual = target.ClientInitMessage;
            Assert.Inconclusive("Verify the correctness of this test method.");
        }
        /// 
        ///A test for UnRegisterMessage
        ///
        [TestMethod()]
        [Ignore]
        public void UnRegisterMessageTest()
        {
            ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
            Session target = new Session(connectionInfo); // TODO: Initialize to an appropriate value
            string messageName = string.Empty; // TODO: Initialize to an appropriate value
            target.UnRegisterMessage(messageName);
            Assert.Inconclusive("A method that does not return a value cannot be verified.");
        }
        /// 
        ///A test for RegisterMessage
        ///
        [TestMethod()]
        [Ignore]
        public void RegisterMessageTest()
        {
            ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
            Session target = new Session(connectionInfo); // TODO: Initialize to an appropriate value
            string messageName = string.Empty; // TODO: Initialize to an appropriate value
            target.RegisterMessage(messageName);
            Assert.Inconclusive("A method that does not return a value cannot be verified.");
        }
        /// 
        ///A test for Dispose
        ///
        [TestMethod()]
        [Ignore]
        public void DisposeTest()
        {
            ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
            Session target = new Session(connectionInfo); // TODO: Initialize to an appropriate value
            target.Dispose();
            Assert.Inconclusive("A method that does not return a value cannot be verified.");
        }
        /// 
        ///A test for Disconnect
        ///
        [TestMethod()]
        [Ignore]
        public void DisconnectTest()
        {
            ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
            Session target = new Session(connectionInfo); // TODO: Initialize to an appropriate value
            target.Disconnect();
            Assert.Inconclusive("A method that does not return a value cannot be verified.");
        }
        /// 
        ///A test for Connect
        ///
        [TestMethod()]
        [Ignore]
        public void ConnectTest()
        {
            ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
            Session target = new Session(connectionInfo); // TODO: Initialize to an appropriate value
            target.Connect();
            Assert.Inconclusive("A method that does not return a value cannot be verified.");
        }
        private static ConnectionInfo CreateConnectionInfo(IPEndPoint serverEndPoint, TimeSpan timeout)
        {
            var connectionInfo = new ConnectionInfo(
                serverEndPoint.Address.ToString(),
                serverEndPoint.Port,
                "eric",
                new NoneAuthenticationMethod("eric"));
            connectionInfo.Timeout = timeout;
            return connectionInfo;
        }
    }
}