jenkinsci/hpe-application-automation-tools-plugin

View on GitHub
HpToolsLauncher/TestRunners/GuiTestRunner.cs

Summary

Maintainability
B
5 hrs
Test Coverage
/*
 * Certain versions of software accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company.
 * This software was acquired by Micro Focus on September 1, 2017, and is now offered by OpenText.
 * Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.
 * __________________________________________________________________
 * MIT License
 *
 * Copyright 2012-2024 Open Text
 *
 * The only warranties for products and services of Open Text and
 * its affiliates and licensors ("Open Text") are as may be set forth
 * in the express warranty statements accompanying such products and services.
 * Nothing herein should be construed as constituting an additional warranty.
 * Open Text shall not be liable for technical or editorial errors or
 * omissions contained herein. The information contained herein is subject
 * to change without notice.
 *
 * Except as specifically indicated otherwise, this document contains
 * confidential information and a valid license is required for possession,
 * use or copying. If this work is provided to the U.S. Government,
 * consistent with FAR 12.211 and 12.212, Commercial Computer Software,
 * Computer Software Documentation, and Technical Data for Commercial Items are
 * licensed to the U.S. Government under vendor's standard commercial license.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ___________________________________________________________________
 */

using HpToolsLauncher.TestRunners;
using HpToolsLauncher.Utils;
using Microsoft.Win32;
using QTObjectModelLib;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Resources = HpToolsLauncher.Properties.Resources;
using AuthType = HpToolsLauncher.McConnectionInfo.AuthType;
using DigitalLabType = HpToolsLauncher.McConnectionInfo.DigitalLabType;

namespace HpToolsLauncher
{
    public class GuiTestRunner : IFileSysTestRunner
    {
        // Setting keys for mobile
        private const string MC_TYPE = "MobileCenterType";
        private const string MOBILE_HOST_ADDRESS = "ALM_MobileHostAddress";
        private const string MOBILE_HOST_PORT = "ALM_MobileHostPort";
        private const string MOBILE_USER = "ALM_MobileUserName";
        private const string MOBILE_PASSWORD = "ALM_MobilePassword";
        private const string MOBILE_CLIENTID = "EXTERNAL_MobileClientID";
        private const string MOBILE_SECRET = "EXTERNAL_MobileSecretKey";
        private const string MOBILE_AUTH_TYPE = "EXTERNAL_MobileAuthType";
        private const string MOBILE_TENANT = "EXTERNAL_MobileTenantId";
        private const string MOBILE_USE_SSL = "ALM_MobileUseSSL";
        private const string MOBILE_USE_PROXY = "EXTERNAL_MobileProxySetting_UseProxy";
        private const string MOBILE_PROXY_SETTING = "EXTERNAL_MobileProxySetting";
        private const string MOBILE_PROXY_SETTING_ADDRESS = "EXTERNAL_MobileProxySetting_Address";
        private const string MOBILE_PROXY_SETTING_PORT = "EXTERNAL_MobileProxySetting_Port";
        private const string MOBILE_PROXY_SETTING_AUTHENTICATION = "EXTERNAL_MobileProxySetting_Authentication";
        private const string MOBILE_PROXY_SETTING_USERNAME = "EXTERNAL_MobileProxySetting_UserName";
        private const string MOBILE_PROXY_SETTING_PASSWORD = "EXTERNAL_MobileProxySetting_Password";
        private const string MOBILE_INFO = "mobileinfo";
        private const string REPORT = "Report";
        private const string READY = "Ready";
        private const string WAITING = "Waiting";
        private const string BUSY = "Busy";
        private const string RUNNING = "Running";
        private const string PASSED = "Passed";
        private const string WARNING = "Warning";
        private const int MEMBER_NOT_FOUND = -2147352573;
        private const string PROTECT_BstrToBase64_FAILED = "ProtectBSTRToBase64 failed for {0}.";
        private const string WEB = "Web";
        private const string CLOUD_BROWSER = "CloudBrowser";
        private const string SYSTEM_PROXY = "System Proxy";
        private const string HTTP_PROXY = "HTTP Proxy";
        private const string DEFAULT_WORKSPACE = "default workspace";

        private readonly Type _qtType = Type.GetTypeFromProgID("Quicktest.Application");
        private readonly IAssetRunner _runNotifier;
        private readonly object _lockObject = new object();
        private TimeSpan _timeLeftUntilTimeout = TimeSpan.MaxValue;
        private readonly string _uftRunMode;
        private Stopwatch _stopwatch = null;
        private Application _qtpApplication;
        private ParameterDefinitions _qtpParamDefs;
        private Parameters _qtpParameters;
        private bool _useUFTLicense;
        private RunCancelledDelegate _runCancelled;
        private McConnectionInfo _mcConnection;
        private string _mobileInfo;
        private CloudBrowser _cloudBrowser;
        private bool _printInputParams;
        private bool _isCancelledByUser;
        private RunAsUser _uftRunAsUser;

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="runNotifier"></param>
        /// <param name="useUftLicense"></param>
        /// <param name="timeLeftUntilTimeout"></param>
        public GuiTestRunner(IAssetRunner runNotifier, bool useUftLicense, TimeSpan timeLeftUntilTimeout, string uftRunMode, DigitalLab digitalLab, bool printInputParams, RunAsUser uftRunAsUser)
        {
            _timeLeftUntilTimeout = timeLeftUntilTimeout;
            _uftRunMode = uftRunMode;
            _stopwatch = Stopwatch.StartNew();
            _runNotifier = runNotifier;
            _useUFTLicense = useUftLicense;
            _mcConnection = digitalLab.ConnectionInfo;
            _mobileInfo = digitalLab.MobileInfo;
            _cloudBrowser = digitalLab.CloudBrowser;
            _printInputParams = printInputParams;
            _uftRunAsUser = uftRunAsUser;
        }

        #region QTP

        /// <summary>
        /// runs the given test and returns resutls
        /// </summary>
        /// <param name="testPath"></param>
        /// <param name="errorReason"></param>
        /// <param name="runCancelled"></param>
        /// <returns></returns>
        public TestRunResults RunTest(TestInfo testinf, ref string errorReason, RunCancelledDelegate runCancelled, out Dictionary<string, string> outParams)
        {
            outParams = new Dictionary<string, string>();
            var testPath = testinf.TestPath;
            TestRunResults runDesc = new TestRunResults { TestType = TestType.QTP };
            ConsoleWriter.ActiveTestRun = runDesc;
            ConsoleWriter.WriteLineWithTime("Running: " + testPath);

            runDesc.TestPath = testPath;

            // default report location is the test path
            runDesc.ReportLocation = testPath;
            // check if the report path has been defined
            if (!string.IsNullOrEmpty(testinf.ReportPath))
            {
                if (!Helper.TrySetTestReportPath(runDesc, testinf, ref errorReason))
                {
                    return runDesc;
                }
            }

            runDesc.TestState = TestState.Unknown;

            _runCancelled = runCancelled;

            if (!Helper.IsQtpInstalled())
            {
                runDesc.TestState = TestState.Error;
                runDesc.ErrorDesc = string.Format(Resources.GeneralQtpNotInstalled, Environment.MachineName);
                ConsoleWriter.WriteErrLine(runDesc.ErrorDesc);
                Environment.ExitCode = (int)Launcher.ExitCodeEnum.Failed;
                return runDesc;
            }

            string reason;
            if (!Helper.CanUftProcessStart(out reason))
            {
                runDesc.TestState = TestState.Error;
                runDesc.ErrorDesc = reason;
                ConsoleWriter.WriteErrLine(runDesc.ErrorDesc);
                Environment.ExitCode = (int)Launcher.ExitCodeEnum.Failed;
                return runDesc;
            }

            Version qtpVersion;
            try
            {
                lock (_lockObject)
                {
                    _qtpApplication = Activator.CreateInstance(_qtType) as Application;
                    if (_uftRunAsUser != null)
                    {
                        try
                        {
                            if (_qtpApplication.Launched)
                            {
                                QTPTestCleanup();
                                CleanUpAndKillQtp();
                            }
                            _qtpApplication.LaunchAsUser(_uftRunAsUser.Username, _uftRunAsUser.EncodedPassword);
                        }
                        catch (COMException e)
                        {
                            if (e.ErrorCode == MEMBER_NOT_FOUND)
                            {
                                errorReason = Resources.UftLaunchAsUserNotSupported;
                            }
                            throw;
                        }
                        if (_qtpApplication.Visible)
                        {
                            _qtpApplication.Visible = false;
                        }
                    }

                    qtpVersion = Version.Parse(_qtpApplication.Version);
                    if (qtpVersion.Equals(new Version(11, 0)))
                    {
                        runDesc.ReportLocation = GetReportLocation(testinf, testPath);
                    }
#if DEBUG
                    Console.WriteLine(string.Format("UFT One version = {0}", qtpVersion));
#endif
                    // Check for required Addins
                    LoadNeededAddins(testPath);

                    SetMobileInfo(qtpVersion);

                    if (!_qtpApplication.Launched)
                    {
                        if (_runCancelled())
                        {
                            QTPTestCleanup();
                            CleanUpAndKillQtp();
                            runDesc.TestState = TestState.Error;
                            return runDesc;
                        }
                        // Launch application after set Addins
                        _qtpApplication.Launch();
                        _qtpApplication.Visible = false;
                    }
                }
            }
            catch (Exception e)
            {
                if (string.IsNullOrEmpty(errorReason))
                {
                    errorReason = Resources.QtpNotLaunchedError;
                    runDesc.ErrorDesc = e.Message;
                    ConsoleWriter.WriteErrLine(e.Message);
                }
                else
                {
                    runDesc.ErrorDesc = errorReason;
                    ConsoleWriter.WriteErrLine(errorReason);
                }
#if DEBUG
                ConsoleWriter.WriteException(e);
#endif
                runDesc.TestState = TestState.Error;
                runDesc.ReportLocation = string.Empty;
                return runDesc;
            }

            if (_qtpApplication.Test != null && _qtpApplication.Test.Modified)
            {
                var message = Resources.QtpNotLaunchedError;
                errorReason = message;
                runDesc.TestState = TestState.Error;
                runDesc.ErrorDesc = errorReason;
                return runDesc;
            }

            _qtpApplication.UseLicenseOfType(_useUFTLicense ? tagUnifiedLicenseType.qtUnifiedFunctionalTesting : tagUnifiedLicenseType.qtNonUnified);

            Dictionary<string, object> paramDict;
            try
            {
                paramDict = testinf.GetParameterDictionaryForQTP();
            }
            catch (ArgumentException)
            {
                ConsoleWriter.WriteErrLine(Resources.FsDuplicateParamNames);
                throw;
            }

            //if (!HandleDigitalLab(qtpVersion, ref errorReason))
            //{
            //    runDesc.TestState = TestState.Error;
            //    runDesc.ErrorDesc = errorReason;
            //    return runDesc;
            //}

            if (!HandleInputParameters(testPath, ref errorReason, paramDict, testinf))
            {
                runDesc.TestState = TestState.Error;
                runDesc.ErrorDesc = errorReason;
                return runDesc;
            }

            if (!HandleCloudBrowser(qtpVersion, ref errorReason))
            {
                ConsoleWriter.WriteErrLine(errorReason);
                runDesc.TestState = TestState.Error;
                runDesc.ErrorDesc = errorReason;
                return runDesc;
            }

            GuiTestRunResult guiTestRunResult = ExecuteQTPRun(runDesc);
            runDesc.ReportLocation = guiTestRunResult.ReportPath;
            if (!guiTestRunResult.IsSuccess)
            {
                runDesc.TestState = TestState.Error;
                return runDesc;
            }

            if (!HandleOutputArguments(ref errorReason, out outParams))
            {
                runDesc.TestState = TestState.Error;
                runDesc.ErrorDesc = errorReason;
                return runDesc;
            }

            QTPTestCleanup();
            _qtpApplication = null;

            return runDesc;
        }

        private string GetReportLocation(TestInfo testinf, string testPath)
        {
            // use the defined report path if provided
            string rptLocation = string.IsNullOrEmpty(testinf.ReportPath) ?
                            Path.Combine(testPath, REPORT) :
                            Path.Combine(testinf.ReportPath, REPORT);

            if (Directory.Exists(rptLocation))
            {
                int lastIndex = rptLocation.IndexOf("\\");
                var location = rptLocation.Substring(0, lastIndex);
                var name = rptLocation.Substring(lastIndex + 1);
                rptLocation = Helper.GetNextResFolder(location, name);
                Directory.CreateDirectory(rptLocation);
            }
            return rptLocation;
        }

        private void SetMobileInfo(Version qtpVersion)
        {
            if (_mcConnection == null || _mcConnection.HostAddress.IsNullOrEmpty())
                return;

            #region Mc connection and other mobile info

            ITDPierToTulip tulip = _qtpApplication.TDPierToTulip;
            //if (qtpVersion < new Version(2023, 4)) // for version >= 23.4 use the method HandleDigitalLab
            {
                tulip.SetTestOptionsVal(MC_TYPE, (int)_mcConnection.LabType);

                tulip.SetTestOptionsVal(MOBILE_HOST_ADDRESS, _mcConnection.HostAddress);
                if (!_mcConnection.HostPort.IsNullOrEmpty())
                {
                    tulip.SetTestOptionsVal(MOBILE_HOST_PORT, _mcConnection.HostPort);
                }

                AuthType mcAuthType = _mcConnection.MobileAuthType;
                tulip.SetTestOptionsVal(MOBILE_AUTH_TYPE, mcAuthType);
                switch (mcAuthType)
                {
                    case AuthType.AuthToken:
                        var token = _mcConnection.GetAuthToken();
                        tulip.SetTestOptionsVal(MOBILE_CLIENTID, token.ClientId);
                        tulip.SetTestOptionsVal(MOBILE_SECRET, token.SecretKey);
                        break;
                    case AuthType.UsernamePassword:
                        if (!_mcConnection.UserName.IsNullOrEmpty())
                        {
                            tulip.SetTestOptionsVal(MOBILE_USER, _mcConnection.UserName);
                            if (!_mcConnection.Password.IsNullOrEmpty())
                            {
                                tulip.SetTestOptionsVal(MOBILE_PASSWORD, GetEncryptedPassword(_mcConnection.Password));
                            }
                        }
                        break;
                }

                // set tenantID
                if (!_mcConnection.TenantId.IsNullOrEmpty())
                {
                    tulip.SetTestOptionsVal(MOBILE_TENANT, _mcConnection.TenantId);
                }

                // ssl and proxy info
                tulip.SetTestOptionsVal(MOBILE_USE_SSL, _mcConnection.UseSslAsInt);

                if (_mcConnection.UseProxy)
                {
                    tulip.SetTestOptionsVal(MOBILE_USE_PROXY, _mcConnection.UseProxyAsInt);
                    tulip.SetTestOptionsVal(MOBILE_PROXY_SETTING, _mcConnection.ProxyType);
                    tulip.SetTestOptionsVal(MOBILE_PROXY_SETTING_ADDRESS, _mcConnection.ProxyAddress);
                    tulip.SetTestOptionsVal(MOBILE_PROXY_SETTING_PORT, _mcConnection.ProxyPort);
                    if (_mcConnection.UseProxyAuth)
                    {
                        tulip.SetTestOptionsVal(MOBILE_PROXY_SETTING_AUTHENTICATION, 1);
                        tulip.SetTestOptionsVal(MOBILE_PROXY_SETTING_USERNAME, _mcConnection.ProxyUserName);
                        tulip.SetTestOptionsVal(MOBILE_PROXY_SETTING_PASSWORD, GetEncryptedPassword(_mcConnection.ProxyPassword));
                    }
                }
            }

            if (!string.IsNullOrEmpty(_mobileInfo))
            {
                tulip.SetTestOptionsVal(MOBILE_INFO, _mobileInfo);
            }

            #endregion
        }

        private string GetEncryptedPassword(string clearPassword)
        {
            string encPassword = WinUserNativeMethods.ProtectBSTRToBase64(clearPassword);
            if (encPassword == null)
            {
                ConsoleWriter.WriteLine(string.Format(PROTECT_BstrToBase64_FAILED, "DL Password"));
                throw new Exception(string.Format(PROTECT_BstrToBase64_FAILED, "DL Password"));
            }
            return encPassword;
        }

        /// <summary>
        /// performs global cleanup code for this type of runner
        /// </summary>
        public void CleanUp()
        {
            try
            {
                lock (_lockObject)
                {
                    //if we don't have a qtp instance, create one
                    if (_qtpApplication == null)
                    {
                        _qtpApplication = Activator.CreateInstance(_qtType) as Application;
                    }

                    _qtpApplication.Quit();
                }
            }
            catch
            {
                //nothing to do. (cleanup code should not throw exceptions, and there is no need to log this as an error in the test)
            }
        }

        public void SafelyCancel()
        {
            _isCancelledByUser = true;
            ConsoleWriter.WriteLine(Resources.GeneralStopAborted);
            QTPTestCleanup();
            CleanUpAndKillQtp();
            ConsoleWriter.WriteLine(Resources.GeneralAbortedByUser);
        }

        static HashSet<string> _colLoadedAddinNames = null;
        /// <summary>
        /// Set the test Addins 
        /// </summary>
        private void LoadNeededAddins(string fileName)
        {
            bool blnNeedToLoadAddins = false;

            //if not launched, we have no addins.
            if (!_qtpApplication.Launched)
                _colLoadedAddinNames = null;

            try
            {
                HashSet<string> colCurrentTestAddins = new HashSet<string>();

                object erroDescription;
                var testAddinsObj = _qtpApplication.GetAssociatedAddinsForTest(fileName);
                object[] testAddins = (object[])testAddinsObj;

                foreach (string addin in testAddins)
                {
                    colCurrentTestAddins.Add(addin);
                }

                if (_colLoadedAddinNames != null)
                {
                    //check if we have a missing addin (and need to quit Qtp, and reload with new addins)
                    foreach (string addin in testAddins)
                    {
                        if (!_colLoadedAddinNames.Contains(addin))
                        {
                            blnNeedToLoadAddins = true;
                            break;
                        }
                    }

                    //check if there is no extra addins that need to be removed
                    if (_colLoadedAddinNames.Count != colCurrentTestAddins.Count)
                    {
                        blnNeedToLoadAddins = true;
                    }
                }
                else
                {
                    //first time = load addins.
                    blnNeedToLoadAddins = true;
                }

                _colLoadedAddinNames = colCurrentTestAddins;

                //the addins need to be refreshed, load new addins
                if (blnNeedToLoadAddins)
                {
                    if (_qtpApplication.Launched && _uftRunAsUser == null)
                        _qtpApplication.Quit();
                    _qtpApplication.SetActiveAddins(ref testAddinsObj, out erroDescription);
                }
            }
            catch (Exception)
            {
                // Try anyway to run the test
            }
        }


        /// <summary>
        /// Activate all Installed Addins 
        /// </summary>
        private void ActivateAllAddins()
        {
            try
            {
                // Get Addins collection
                Addins qtInstalledAddins = _qtpApplication.Addins;

                if (qtInstalledAddins.Count > 0)
                {
                    string[] qtAddins = new string[qtInstalledAddins.Count];

                    // Addins Object is 1 base order
                    for (int idx = 1; idx <= qtInstalledAddins.Count; ++idx)
                    {
                        // Our list is 0 base order
                        qtAddins[idx - 1] = qtInstalledAddins[idx].Name;
                    }

                    object erroDescription;
                    var addinNames = (object)qtAddins;

                    _qtpApplication.SetActiveAddins(ref addinNames, out erroDescription);
                }
            }
            catch (Exception)
            {
                // Try anyway to run the test
            }
        }

        /// <summary>
        /// runs the given test QTP and returns results
        /// </summary>
        /// <param name="testResults">the test results object containing test info and also receiving run results</param>
        /// <returns></returns>
        private GuiTestRunResult ExecuteQTPRun(TestRunResults testResults)
        {
            GuiTestRunResult result = new GuiTestRunResult { IsSuccess = true };
            try
            {
                Type runResultsOptionstype = Type.GetTypeFromProgID("QuickTest.RunResultsOptions");
                var options = (RunResultsOptions)Activator.CreateInstance(runResultsOptionstype);
                options.ResultsLocation = testResults.ReportLocation;
                if (_uftRunMode != null)
                {
                    _qtpApplication.Options.Run.RunMode = _uftRunMode;
                }

                //Check for cancel before executing
                if (_runCancelled())
                {
                    QTPTestCleanup();
                    CleanUpAndKillQtp();
                    testResults.TestState = TestState.Error;
                    testResults.ErrorDesc = Resources.GeneralTestCanceled;
                    ConsoleWriter.WriteLine(Resources.GeneralTestCanceled);
                    result.IsSuccess = false;
                    Launcher.ExitCode = Launcher.ExitCodeEnum.Aborted;
                    return result;
                }
                ConsoleWriter.WriteLine(string.Format(Resources.FsRunnerRunningTest, testResults.TestPath));

                _qtpApplication.Test.Run(options, false, _qtpParameters);

                result.ReportPath = Path.Combine(testResults.ReportLocation, REPORT);
                int slept = 0;
                while ((slept < 20000 && _qtpApplication.GetStatus() == READY) || _qtpApplication.GetStatus() == WAITING)
                {
                    Thread.Sleep(50);
                    slept += 50;
                }

                while (!_runCancelled() && (_qtpApplication.GetStatus() == RUNNING || _qtpApplication.GetStatus() == BUSY))
                {
                    Thread.Sleep(200);
                    if (_timeLeftUntilTimeout - _stopwatch.Elapsed <= TimeSpan.Zero)
                    {
                        QTPTestCleanup();
                        CleanUpAndKillQtp();
                        testResults.TestState = TestState.Error;
                        testResults.ErrorDesc = Resources.GeneralTimeoutExpired;
                        ConsoleWriter.WriteLine(Resources.GeneralTimeoutExpired);
                        Launcher.ExitCode = Launcher.ExitCodeEnum.Aborted;
                        result.IsSuccess = false;
                        return result;
                    }
                }

                if (_runCancelled())
                {
                    QTPTestCleanup();
                    CleanUpAndKillQtp();
                    testResults.TestState = TestState.Error;
                    testResults.ErrorDesc = Resources.GeneralTestCanceled;
                    ConsoleWriter.WriteLine(Resources.GeneralTestCanceled);
                    Launcher.ExitCode = Launcher.ExitCodeEnum.Aborted;
                    result.IsSuccess = false;
                    return result;
                }
                string lastError = _qtpApplication.Test.LastRunResults.LastError;

                //read the lastError
                if (!string.IsNullOrEmpty(lastError))
                {
                    testResults.TestState = TestState.Error;
                    testResults.ErrorDesc = lastError;
                }

                // the way to check the logical success of the target QTP test is: app.Test.LastRunResults.Status == "Passed".
                if (_qtpApplication.Test.LastRunResults.Status == PASSED)
                {
                    testResults.TestState = TestState.Passed;
                }
                else if (_qtpApplication.Test.LastRunResults.Status == WARNING)
                {
                    testResults.TestState = TestState.Warning;
                    testResults.HasWarnings = true;

                    if (Launcher.ExitCode != Launcher.ExitCodeEnum.Failed && Launcher.ExitCode != Launcher.ExitCodeEnum.Aborted)
                        Launcher.ExitCode = Launcher.ExitCodeEnum.Unstable;
                }
                else
                {
                    testResults.TestState = TestState.Failed;
                    testResults.FailureDesc = "Test failed";

                    Launcher.ExitCode = Launcher.ExitCodeEnum.Failed;
                }
            }
            catch (NullReferenceException e)
            {
                ConsoleWriter.WriteLine(string.Format(Resources.GeneralErrorWithStack, e.Message, e.StackTrace));
                testResults.TestState = TestState.Error;
                testResults.ErrorDesc = Resources.QtpRunError;

                result.IsSuccess = false;
                return result;
            }
            catch (SystemException e)
            {
                CleanUpAndKillQtp();
                ConsoleWriter.WriteLine(string.Format(Resources.GeneralErrorWithStack, e.Message, e.StackTrace));
                testResults.TestState = TestState.Error;
                testResults.ErrorDesc = Resources.QtpRunError;

                result.IsSuccess = false;
                return result;
            }
            catch (Exception e2)
            {
                if (_isCancelledByUser)
                {
                    testResults.TestState = TestState.Error;
                    testResults.ErrorDesc = Resources.GeneralStopAborted;
                }
                else
                {
                    ConsoleWriter.WriteLine(string.Format(Resources.GeneralErrorWithStack, e2.Message, e2.StackTrace));
                    testResults.TestState = TestState.Error;
                    testResults.ErrorDesc = Resources.QtpRunError;
                }

                result.IsSuccess = false;
                return result;
            }

            return result;
        }

        private void CleanUpAndKillQtp()
        {
            //error during run, process may have crashed (need to cleanup, close QTP and qtpRemote for next test to run correctly)
            CleanUp();

            //kill the qtp automation, to make sure it will run correctly next time
            Process[] processes = Process.GetProcessesByName("qtpAutomationAgent");
            Process qtpAuto = processes.Where(p => p.SessionId == Process.GetCurrentProcess().SessionId).FirstOrDefault();
            if (qtpAuto != null)
            {
                qtpAuto.Kill();
            }
        }
        private bool HandleDigitalLab(Version qtpVersion, ref string errorReason)
        {
            if (_mcConnection == null || _mcConnection.HostAddress.IsNullOrEmpty() || qtpVersion < new Version(2023, 4))
                return true;

            return SetDLOptions(_qtpApplication.Options.DLConnection, ref errorReason);
        }

        private bool SetDLOptions(DLConnectionOptions opt, ref string errorReason)
        {
            try
            {
                int type = (int)DigitalLabType.UFT; // TODO set and get it from props
                opt.Type = type.ToString();
                if (_mcConnection.MobileAuthType == AuthType.AuthToken)
                {
                    opt.AuthType = AuthType.AuthToken.GetEnumDescription();
                    opt.AccessKey = _mcConnection.ExecToken;
                }
                else if (!_mcConnection.UserName.IsNullOrEmpty())
                {
                    opt.AuthType = AuthType.UsernamePassword.GetEnumDescription();
                    opt.UserName = _mcConnection.UserName;
                    opt.Password = _mcConnection.Password;
                }
                opt.Server = _mcConnection.HostAddress;
                opt.Port = _mcConnection.HostPort;
                opt.UseSSL = _mcConnection.UseSSL;
                if (_mcConnection.UseProxy)
                {
                    opt.UseProxySettings = true;
                    opt.ProxyType = _mcConnection.ProxyType == 1 ? SYSTEM_PROXY : HTTP_PROXY;
                    opt.ProxyAddress = _mcConnection.ProxyAddress;
                    opt.ProxyPort = _mcConnection.ProxyPort.ToString();
                    if (_mcConnection.UseProxyAuth)
                    {
                        opt.SpecifyAuthentication = true;
                        opt.ProxyUserName = _mcConnection.ProxyUserName;
                        opt.ProxyPassword = _mcConnection.ProxyPassword;
                    }
                }
                opt.ShowRemoteWndOnRun = true;
                opt.WorkSpace = DEFAULT_WORKSPACE;
                return true;
            }
            catch (Exception ex)
            {
#if DEBUG
                Console.WriteLine(ex.Message);
#endif
                errorReason = ex.Message;
                return false;
            }
        }

        private bool HandleOutputArguments(ref string errorReason, out Dictionary<string, string> outParams)
        {
            outParams = new Dictionary<string, string>();
            try
            {
                for (int i = 1; i <= _qtpParamDefs.Count; ++i)
                {
                    var pd = _qtpParamDefs[i];
                    if (pd.InOut == qtParameterDirection.qtParamDirOut)
                    {
                        var value = _qtpParameters[pd.Name].Value;
                        if (value != null)
                        {
                            if (outParams.ContainsKey(pd.Name))
                            {
                                outParams[pd.Name] = value.ToString();
                            }
                            else
                            {
                                outParams.Add(pd.Name, value.ToString());
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {
                errorReason = Resources.QtpNotLaunchedError;
                return false;
            }
            return true;
        }
        private bool VerifyParameterValueType(object paramValue, qtParameterType type)
        {
            bool legal = false;

            switch (type)
            {
                case qtParameterType.qtParamTypeBoolean:
                    legal = paramValue is bool;
                    break;

                case qtParameterType.qtParamTypeDate:
                    legal = paramValue is DateTime;
                    break;

                case qtParameterType.qtParamTypeNumber:
                    legal = ((paramValue is int) || (paramValue is long) || (paramValue is decimal) || (paramValue is float) || (paramValue is double));
                    break;

                case qtParameterType.qtParamTypePassword:
                case qtParameterType.qtParamTypeString:
                case qtParameterType.qtParamTypeAny:
                    legal = paramValue is string;
                    break;

                default:
                    legal = false;
                    break;
            }

            return legal;
        }

        private bool HandleCloudBrowser(Version qtpVersion, ref string errorReason)
        {
            if (_cloudBrowser != null)
            {
                if (qtpVersion < new Version(2023, 4))
                {
                    errorReason = string.Format(Resources.CloudBrowserNotSupported, qtpVersion.ToString(2));
                    return false;
                }
                try
                {
                    var launcher = _qtpApplication.Test.Settings.Launchers[WEB];
                    launcher.Active = true;
                    launcher.SetLab(CLOUD_BROWSER);
                    if (!_cloudBrowser.Url.IsNullOrWhiteSpace())
                        launcher.Address = _cloudBrowser.Url;
                    launcher.CloudBrowser.OS = _cloudBrowser.OS;
                    launcher.CloudBrowser.Browser = _cloudBrowser.Browser;
                    launcher.CloudBrowser.BrowserVersion = _cloudBrowser.Version;
                    launcher.CloudBrowser.Location = _cloudBrowser.Region;
                }
                catch (Exception e)
                {
                    ConsoleWriter.WriteLine(string.Format(Resources.GeneralErrorWithStack, e.Message, e.StackTrace));
                    errorReason = e.Message;
                    return false;
                }
            }
            return true;
        }

        private bool HandleInputParameters(string fileName, ref string errorReason, Dictionary<string, object> inputParams, TestInfo testInfo)
        {
            try
            {
                string path = fileName;

                if (_runCancelled())
                {
                    QTPTestCleanup();
                    CleanUpAndKillQtp();
                    return false;
                }

                _qtpApplication.Open(path, true, false);
                _qtpParamDefs = _qtpApplication.Test.ParameterDefinitions;
                _qtpParameters = _qtpParamDefs.GetParameters();

                // handle all parameters (index starts with 1 !!!)
                for (int i = 1; i <= _qtpParamDefs.Count; i++)
                {
                    // input parameters
                    if (_qtpParamDefs[i].InOut == qtParameterDirection.qtParamDirIn)
                    {
                        string paramName = _qtpParamDefs[i].Name;
                        qtParameterType type = _qtpParamDefs[i].Type;

                        // if the caller supplies value for a parameter we set it
                        if (!inputParams.ContainsKey(paramName)) continue;

                        // first verify that the type is correct
                        object paramValue = inputParams[paramName];
                        if (!VerifyParameterValueType(paramValue, type))
                        {
                            try
                            {
                                ConsoleWriter.WriteErrLine(string.Format(Resources.GeneralParameterTypeMismatchWith2Types, paramName, Enum.GetName(typeof(qtParameterType), type), paramValue.GetType()));
                            }
                            catch (Exception)
                            {
                                ConsoleWriter.WriteErrLine(string.Format(Resources.GeneralParameterTypeMismatchWith2Types, paramName, Enum.GetName(typeof(qtParameterType), type), null));
                            }
                        }
                        else
                        {
                            // second-check
                            try
                            {
                                _qtpParameters[paramName].Value = paramValue;
                                if (_printInputParams)
                                {
                                    if (type == qtParameterType.qtParamTypePassword)
                                        ConsoleWriter.WriteLine(string.Format(Resources.GeneralParameterUsageMask, paramName));
                                    else
                                        ConsoleWriter.WriteLine(string.Format(Resources.GeneralParameterUsage, paramName, type != qtParameterType.qtParamTypeDate ? paramValue : ((DateTime)paramValue).ToShortDateString()));
                                }
                            }
                            catch (Exception)
                            {
                                ConsoleWriter.WriteErrLine(string.Format(Resources.GeneralParameterTypeMismatchWith1Type, paramName));
                            }
                        }
                    }
                }

                // specify data table path
                if (testInfo.DataTablePath != null)
                {
                    _qtpApplication.Test.Settings.Resources.DataTablePath = testInfo.DataTablePath;
                    ConsoleWriter.WriteLine("Using external data table: " + testInfo.DataTablePath);
                }

                // specify iteration mode
                if (testInfo.IterationInfo != null)
                {
                    try
                    {
                        IterationInfo ii = testInfo.IterationInfo;
                        if (!IterationInfo.AvailableTypes.Contains(ii.IterationMode))
                        {
                            throw new ArgumentException(string.Format("Illegal iteration mode '{0}'. Available modes are : {1}", ii.IterationMode, string.Join(", ", IterationInfo.AvailableTypes)));
                        }

                        bool rangeMode = IterationInfo.RANGE_ITERATION_MODE.Equals(ii.IterationMode);
                        if (rangeMode)
                        {
                            int start = int.Parse(ii.StartIteration);
                            int end = int.Parse(ii.EndIteration);

                            _qtpApplication.Test.Settings.Run.StartIteration = start;
                            _qtpApplication.Test.Settings.Run.EndIteration = end;
                        }

                        _qtpApplication.Test.Settings.Run.IterationMode = testInfo.IterationInfo.IterationMode;

                        ConsoleWriter.WriteLine("Using iteration mode: " + testInfo.IterationInfo.IterationMode +
                       (rangeMode ? " " + testInfo.IterationInfo.StartIteration + "-" + testInfo.IterationInfo.EndIteration : string.Empty));
                    }
                    catch (Exception e)
                    {
                        string msg = "Failed to parse 'Iterations' element . Using default iteration settings. Error : " + e.Message;
                        ConsoleWriter.WriteLine(msg);
                    }
                }
            }
            catch (Exception)
            {
                errorReason = Resources.QtpRunError;
                return false;
            }
            return true;
        }

        /// <summary>
        /// stops and closes qtp test, to make sure nothing is left floating after run.
        /// </summary>
        private void QTPTestCleanup()
        {
            try
            {
                lock (_lockObject)
                {
                    if (_qtpApplication == null)
                    {
                        return;
                    }

                    var qtpTest = _qtpApplication.Test;
                    if (qtpTest != null)
                    {
                        try
                        {
                            _qtpApplication.Test.Stop();
                            _qtpApplication.Test.Close();
                        }
                        catch (Exception)
                        { }
                    }
                }
            }
            catch (Exception)
            {
            }

            _qtpParameters = null;
            _qtpParamDefs = null;
        }

        private RegistryKey GetQuickTestProfessionalAutomationRegKey(RegistryView registryView)
        {
            RegistryKey localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64);
            localKey = localKey.OpenSubKey(@"SOFTWARE\Classes\AppID\{A67EB23A-1B8F-487D-8E38-A6A3DD150F0B}", true);

            return localKey;
        }

        #endregion

        /// <summary>
        /// holds the resutls for a GUI test
        /// </summary>
        private class GuiTestRunResult
        {
            public GuiTestRunResult()
            {
                ReportPath = string.Empty;
            }

            public bool IsSuccess { get; set; }
            public string ReportPath { get; set; }
        }
    }
}