using System;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Renci.SshClient.Tests
{
    /// 
    /// Summary description for UnitTest1
    /// 
    [TestClass]
    public class ShellTest
    {
        public ShellTest()
        {
            //
            // TODO: Add constructor logic here
            //
        }
        private TestContext testContextInstance;
        /// 
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }
        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion
        [TestMethod]
        public void TestConnectUsingPassword()
        {
            var s = CreateShellUsingPassword();
            s.Connect();
            s.Disconnect();
        }
        [TestMethod]
        public void TestExecuteSingleCommand()
        {
            var s = CreateShellUsingPassword();
            s.Connect();
            var result = ExecuteTestCommand(s);
            s.Disconnect();
            Assert.IsTrue(result);
        }
        [TestMethod]
        public void TestReconnecting()
        {
            var s = CreateShellUsingPassword();
            s.Connect();
            var result = ExecuteTestCommand(s);
            s.Disconnect();
            Assert.IsTrue(result);
            s.Connect();
            result = ExecuteTestCommand(s);
            s.Disconnect();
            Assert.IsTrue(result);
        }
        [TestMethod]
        public void TestMultipleThreadMultipleSessions_10000()
        {
            var s = CreateShellUsingPassword();
            s.Connect();
            System.Threading.Tasks.Parallel.For(0, 10000,
                (counter) =>
                {
                    var result = ExecuteTestCommand(s);
                    Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter));
                    Assert.IsTrue(result);
                }
            );
            s.Disconnect();
        }
        [TestMethod]
        public void TestMultipleThreadMultipleConnections_10000()
        {
            try
            {
                System.Threading.Tasks.Parallel.For(0, 10000,
                    () =>
                    {
                        var s = CreateShellUsingPassword();
                        s.Connect();
                        return s;
                    },
                    (int counter, ParallelLoopState pls, SshClient s) =>
                    {
                        var result = ExecuteTestCommand(s);
                        Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter));
                        Assert.IsTrue(result);
                        return s;
                    },
                    (SshClient s) =>
                    {
                        s.Disconnect();
                    }
                );
            }
            catch (Exception exp)
            {
                Assert.Fail(exp.ToString());
            }
        }
        [TestMethod]
        public void TestExtendedOutput()
        {
            var s = CreateShellUsingPassword();
            s.Connect();
            var cmd = s.CreateCommand("echo 12345; echo 654321 >&2");
            cmd.Execute();
            var extendedData = Encoding.ASCII.GetString(cmd.ExtendedOutputStream.ToArray());
            s.Disconnect();
            Assert.AreEqual("12345\n", cmd.Result);
            Assert.AreEqual("654321\n", extendedData);
        }
        [TestMethod]
        public void TestInvalidCommandExecution()
        {
            var s = CreateShellUsingPassword();
            s.Connect();
            var cmd = s.CreateCommand(";");
            cmd.Execute();
            if (string.IsNullOrEmpty(cmd.Error))
            {
                Assert.Fail("Operation should fail");
            }
            Assert.IsTrue(cmd.ExitStatus > 0);
            s.Disconnect();
        }
        [TestMethod]
        public void TestInvalidCommandThenValidCommandExecution()
        {
            var s = CreateShellUsingPassword();
            s.Connect();
            var cmd = s.CreateCommand(";");
            cmd.Execute();
            if (string.IsNullOrEmpty(cmd.Error))
            {
                Assert.Fail("Operation should fail");
            }
            Assert.IsTrue(cmd.ExitStatus > 0);
            var result = ExecuteTestCommand(s);
            s.Disconnect();
            Assert.IsTrue(result);
        }
        [TestMethod]
        public void TestRsaKeyConnection()
        {
            var s = CreateShellUsingRSAKey();
            s.Connect();
            var result = ExecuteTestCommand(s);
            s.Disconnect();
            Assert.IsTrue(result);
        }
        [TestMethod]
        public void TestDssKeyConnection()
        {
            var s = CreateShellUsingRSAKey();
            s.Connect();
            var result = ExecuteTestCommand(s);
            s.Disconnect();
            Assert.IsTrue(result);
        }
        private static SshClient CreateShellUsingPassword()
        {
            return new SshClient(ConnectionData.Host, ConnectionData.Port, ConnectionData.Username, ConnectionData.Password);
        }
        private static SshClient CreateShellUsingRSAKey()
        {
            return new SshClient(ConnectionData.Host, ConnectionData.Port, ConnectionData.Username, new PrivateKeyFile(ConnectionData.RsaKeyFilePath));
        }
        private static SshClient CreateShellUsingDSSKey()
        {
            return new SshClient(ConnectionData.Host, ConnectionData.Port, ConnectionData.Username, new PrivateKeyFile(ConnectionData.DssKeyFilePath));
        }
        private static bool ExecuteTestCommand(SshClient s)
        {
            var testValue = Guid.NewGuid().ToString();
            var command = string.Format("echo {0}", testValue);
            //var command = string.Format("echo {0};sleep 2s", testValue);
            var cmd = s.CreateCommand(command);
            var result = cmd.Execute();
            result = result.Substring(0, result.Length - 1);    //  Remove \n chararacter returned by command
            return result.Equals(testValue);
        }
    }
}