Click here to Skip to main content
15,892,674 members
Articles / Programming Languages / C#

NUnit Starter

Rate me:
Please Sign up or sign in to vote.
4.92/5 (6 votes)
13 Oct 2009Zlib34 min read 42.5K   470   39  
How to create Visual Studio 2008 Add-in compatible with Visual Studio 2003
��/*

 * Filename:    Connect.cs

 * Product:     NUnit Starter

 * Solution:    NUnitStarter.sln

 * Project:     AddIn

 * Description: Entry class for the add-in.

 * Copyright:   Julijan `ribar, 2009

 * 

 * This software is provided 'as-is', without any express or implied

 * warranty.  In no event will the author(s) be held liable for any damages

 * arising from the use of this software.

 *

 * Permission is granted to anyone to use this software for any purpose,

 * including commercial applications, and to alter it and redistribute it

 * freely, subject to the following restrictions:

 *

 * 1. The origin of this software must not be misrepresented; you must not

 *    claim that you wrote the original software. If you use this software

 *    in a product, an acknowledgment in the product documentation would be

 *    appreciated but is not required.

 * 2. Altered source versions must be plainly marked as such, and must not be

 *    misrepresented as being the original software.

 * 3. This notice may not be removed or altered from any source distribution.

 */

using System;

using System.Diagnostics;

using System.Windows.Forms;



using Extensibility;

using EnvDTE;



#if !TARGETTING_FX_1_1

using EnvDTE80;

using _DTE = EnvDTE80.DTE2;

#endif



namespace NUnitStarter

{

	/// <summary>The object for implementing an Add-in.</summary>

	/// <seealso class='IDTExtensibility2' />

	public class Connect : IDTExtensibility2, IDTCommandTarget

	{

        #region Constructors



        /// <summary>

        ///	  Implements the constructor for the Add-in object.

        ///	  Place your initialization code within this method.

        /// </summary>

        public Connect()

        {

        }



        /// <summary>

        ///   Static constructor. Initializes static members, mainly localized

        ///   string resources.

        /// </summary>

        static Connect()

        {

            TextNUnitExeNotFound = "Invalid path(s) to one of or to both NUnit executables." + Environment.NewLine + "Please configure both paths or press Cancel to quit.";

            TextInvalidConfiguration = "Current configuration is not valid." + Environment.NewLine + "Do you want to run automatic configuration?";

            TextApplicationNotConfigured = "Addin is not configured correctly and cannot be used.";

            TextConfigurationSaved = "Configuration successfully saved." + Environment.NewLine + "To change configuration just run Configurator executable.";

        }



        #endregion // Constructors



        #region Public methods



        /// <summary>

        ///    Implements the OnConnection method of the IDTExtensibility2 interface.

        ///    Receives notification that the Add-in is being loaded.

        /// </summary>

        /// <param term='application'>

        ///    Root object of the host application.

        /// </param>

        /// <param term='connectMode'>

        ///    Describes how the Add-in is being loaded.

        /// </param>

        /// <param term='addInInst'>

        ///    Object representing this Add-in.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)

        {

            m_applicationObject = (_DTE)application;

            m_addInInstance = (AddIn)addInInst;

            try

            {

                // for detailed description of actions below check

                // http://www.mztools.com/articles/2005/MZ2005003.aspx

                switch (connectMode)

                {

                    case ext_ConnectMode.ext_cm_UISetup:

                        // Do nothing for this add-in with temporary user interface

                        break;

                    case ext_ConnectMode.ext_cm_Startup:

                        // The add-in was marked to load on startup

                        // Do nothing at this point because the IDE may not be fully initialized

                        // Visual Studio will call OnStartupComplete when fully initialized

                        break;

                    case ext_ConnectMode.ext_cm_AfterStartup:

                        // The add-in was loaded by hand after startup using the Add-In Manager

                        // Initialize it in the same way that when is loaded on startup

                        Initialize();

                        break;

                }

            }

            catch (System.Exception e)

            {

                ExceptionMessageBox.Show(e);

            }

        }



        /// <summary>

        ///     Implements the OnDisconnection method of the IDTExtensibility2 interface.

        ///     Receives notification that the Add-in is being unloaded.

        /// </summary>

        /// <param term='disconnectMode'>

        ///      Describes how the Add-in is being unloaded.

        /// </param>

        /// <param term='custom'>

        ///      Array of parameters that are host application specific.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom)

        {

            try

            {

                // delete UI elements and dispose executor so that NUnit GUI window is closed too

                switch (disconnectMode)

                {

                    case ext_DisconnectMode.ext_dm_HostShutdown:

                    case ext_DisconnectMode.ext_dm_UserClosed:

                        if (m_uiElements != null)

                            m_uiElements.Dispose();

                        if (m_nUnitExecutor != null)

                            m_nUnitExecutor.Dispose();

                        break;

                }

            }

            catch (Exception e)

            {

                ExceptionMessageBox.Show(e);

            }

        }



        /// <summary>

        ///      Implements the OnAddInsUpdate method of the IDTExtensibility2 interface.

        ///      Receives notification that the collection of Add-ins has changed.

        /// </summary>

        /// <param term='custom'>

        ///      Array of parameters that are host application specific.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnAddInsUpdate(ref System.Array custom)

        {

        }



        /// <summary>

        ///      Implements the OnStartupComplete method of the IDTExtensibility2 interface.

        ///      Receives notification that the host application has completed loading.

        /// </summary>

        /// <param term='custom'>

        ///      Array of parameters that are host application specific.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnStartupComplete(ref System.Array custom)

        {

            Initialize();

        }



        /// <summary>

        ///      Implements the OnBeginShutdown method of the IDTExtensibility2 interface.

        ///      Receives notification that the host application is being unloaded.

        /// </summary>

        /// <param term='custom'>

        ///      Array of parameters that are host application specific.

        /// </param>

        /// <seealso class='IDTExtensibility2' />

        public void OnBeginShutdown(ref System.Array custom)

        {

        }



        /// <summary>

        ///      Implements the QueryStatus method of the IDTCommandTarget interface.

        ///      This is called when the command's availability is updated

        /// </summary>

        /// <param term='commandName'>

        ///		The name of the command to determine state for.

        /// </param>

        /// <param term='neededText'>

        ///		Text that is needed for the command.

        /// </param>

        /// <param term='status'>

        ///		The state of the command in the user interface.

        /// </param>

        /// <param term='commandText'>

        ///		Text requested by the neededText parameter.

        /// </param>

        /// <seealso class='Exec' />

        public void QueryStatus(string commandName, EnvDTE.vsCommandStatusTextWanted neededText, ref EnvDTE.vsCommandStatus status, ref object commandText)

        {

            if (neededText == EnvDTE.vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)

            {

                if (commandName == StartNUnitTestCommandName || commandName == DebugNUnitTestCommandName)

                {

                    bool isAvailable = m_nUnitExecutor != null && (commandName != DebugNUnitTestCommandName || m_nUnitExecutor.CanDebugTest);

                    // prevent running tests while debugging

                    bool isDebugging = m_applicationObject.Debugger.CurrentMode != dbgDebugMode.dbgDesignMode;

                    bool isEnabled = isAvailable && !isDebugging;

                    if (isAvailable)

                    {

                        // adjust the state of commands (available/disabled) according to current selection

                        CommandStatus cs = new CommandStatus(m_applicationObject.SelectedItems);

                        isAvailable &= cs.IsAvailable();

                        isEnabled &= cs.IsEnabled();

                    }

                    status = (vsCommandStatus)(isAvailable ? vsCommandStatus.vsCommandStatusSupported : vsCommandStatus.vsCommandStatusInvisible) |

                        (isEnabled ? vsCommandStatus.vsCommandStatusEnabled : vsCommandStatus.vsCommandStatusUnsupported);

                    return;

                }

            }

        }



        /// <summary>

        ///      Implements the Exec method of the IDTCommandTarget interface.

        ///      This is called when the command is invoked.

        /// </summary>

        /// <param term='commandName'>

        ///		The name of the command to execute.

        /// </param>

        /// <param term='executeOption'>

        ///		Describes how the command should be run.

        /// </param>

        /// <param term='varIn'>

        ///		Parameters passed from the caller to the command handler.

        /// </param>

        /// <param term='varOut'>

        ///		Parameters passed from the command handler to the caller.

        /// </param>

        /// <param term='handled'>

        ///		Informs the caller if the command was handled or not.

        /// </param>

        /// <seealso class='Exec' />

        public void Exec(string commandName, EnvDTE.vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)

        {

            handled = false;

            if (executeOption == EnvDTE.vsCommandExecOption.vsCommandExecOptionDoDefault)

            {

                try

                {

                    if (commandName == StartNUnitTestCommandName)

                    {

                        Debug.Assert(m_nUnitExecutor != null);

                        Debug.Assert(m_applicationObject.SelectedItems.Count == 1);

                        m_nUnitExecutor.RunTest(m_applicationObject.SelectedItems.Item(1));

                    }

                    else if (commandName == DebugNUnitTestCommandName)

                    {

                        Debug.Assert(m_nUnitExecutor != null && m_nUnitExecutor.CanDebugTest);

                        Debug.Assert(m_applicationObject.SelectedItems.Count == 1);

                        // launch NUnit and attach VS to its process for debugging

                        m_nUnitExecutor.DebugTest(m_applicationObject, m_applicationObject.SelectedItems.Item(1));

                    }

                }

                catch (ProjectBuildFailedException)

                {

                    // this exception has been processed 

                }

                catch (Exception e)

                {

                    ExceptionMessageBox.Show(e);

                }

                handled = true;

                return;

            }

        }



        #endregion // Public methods



        #region Private properties



        /// <summary>

        ///   Gets full name of start test command.

        /// </summary>

        private string StartNUnitTestCommandName

        {

            get

            {

                return string.Format("{0}.{1}", m_addInInstance.ProgID, UiElements.StartNUnitTestCommandName);

            }

        }



        /// <summary>

        ///   Gets full name of debug test command.

        /// </summary>

        private string DebugNUnitTestCommandName

        {

            get

            {

                return string.Format("{0}.{1}", m_addInInstance.ProgID, UiElements.DebugNUnitTestCommandName);

            }

        }



        #endregion // Private properties



        #region Private methods



        /// <summary>

        ///   Initialize add-in command and UI controls

        /// </summary>

        private void Initialize()

        {

            try

            {

#if TARGETTING_FX_1_1

                // we have to enable visual styles since .NET 1.1 (i.e. VS2003) does not support it natively

                Application.EnableVisualStyles();

#endif

                // check if NUnit is installed at all; in case of error, 

                // NUnitNotInstalledException or FileNotFoundException is thrown

                CreateExecutor();



                // create commands and corresponding controls

                m_uiElements = new UiElements(m_applicationObject, m_addInInstance);

            }

            catch (NUnitNotInstalledException)

            {

                NUnitNotInstalledMessageBox.Show();

                return;

            }

            catch (ApplicationNotConfiguredException)

            {

                MessageBox.Show(TextApplicationNotConfigured, Constants.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);

                return;

            }

            catch (Exception e)

            {

                ExceptionMessageBox.Show(e);

                return;

            }

        }



        /// <summary>

        ///   Create NUnit executor responsible to start NUnit.

        /// </summary>

        private void CreateExecutor()

        {

            NUnitExecutables execs = GetNUnitExecutables();

            Debug.Assert(execs.Configured);

            m_nUnitExecutor = new NUnitExecutor(execs);

        }



        /// <summary>

        ///   Gets nunit executables required to create <c>NUnitExecutor</c>.

        ///   Reads data from configuration file first. If configuration does 

        ///   not exist, is corrupt or invalid, attempts automatic configuration

        ///   first and if that fails, configuration dialog is shown.

        /// </summary>

        /// <returns>

        ///   <c>NUnitFilenames</c> object with nunit executables.

        /// </returns>

        private NUnitExecutables GetNUnitExecutables()

        {

            // try to read configuration first

            Configuration config = new Configuration();

            NUnitExecutables execs = config.Settings.GetExecutablesForCurrentCLR();

            if (execs == null || !execs.Configured)

            {

                if (AskForAutomaticConfiguration(config.State != Configuration.ConfigurationState.OK))

                {

                    // try automatic configuration

                    NUnitLocator nl = new NUnitLocator();

                    execs = nl.NUnitExecutables;

                    config.Settings.SetExecutablesForCurrentCLR(execs);

                }

                // if automatic configuration fails or chosen manual configuration then open dialog

                if (execs == null || !execs.Configured)

                {

                    execs = DoManualConfiguration(config.Settings);

                }

                config.Settings.SetExecutablesForCurrentCLR(execs);

                config.SaveSettings();

                MessageBox.Show(TextConfigurationSaved, Constants.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);

            }

            return execs;

        }



        /// <summary>

        ///   Prompt a message on invalid configuration and ask for automatic

        ///   configuration.

        /// </summary>

        /// <param name="missingOrCorruptConfigFile">

        ///   <c>true</c> if configuration file is missing or corrupt, <c>false</c>

        ///   if configuration is invalid.

        /// </param>

        /// <returns>

        ///   <c>true</c> if user selects automatic configuration, otherwise 

        ///   <c>false</c>.

        /// </returns>

        private bool AskForAutomaticConfiguration(bool missingOrCorruptConfigFile)

        {

            string msg = missingOrCorruptConfigFile ? Constants.TextInvalidOrMissingConfigurationFile : TextInvalidConfiguration;

            return MessageBox.Show(msg, Constants.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes;

        }



        /// <summary>

        ///   Opens form for manual configuration. If configuration fails (i.e.

        ///   user clicks Cancel button, <c>ApplicationNotConfiguredException</c>

        ///   is thrown.

        /// </summary>

        /// <param name="settings">

        ///   <c>Settings</c> object.

        /// </param>

        /// <returns>

        ///   <c>NUnitFilenames</c> object with nunit executables.

        /// </returns>

        private NUnitExecutables DoManualConfiguration(Settings settings)

        {

            NUnitExecutables execs = null;

            ConfigurationForm cf = new ConfigurationForm();

            cf.ShowInTaskbar = false;

            cf.ShowForCurrentRuntimeOnly = true;

            cf.Settings = settings;

            // repeat the loop until correct values are entered

            // clicking Cancel throws exception and breaks the loop

            while (execs == null || !execs.Configured)

            {

                // show configuration form

                if (cf.ShowDialog() == DialogResult.Cancel)

                    throw new ApplicationNotConfiguredException();

                execs = cf.Settings.GetExecutablesForCurrentCLR();

                Debug.Assert(execs != null);

                if (!execs.Configured)

                {

                    if (MessageBox.Show(TextNUnitExeNotFound, Constants.ProductName, MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.Cancel)

                        throw new ApplicationNotConfiguredException();

                }

            }

            Debug.Assert(execs != null && execs.Configured);

            return execs;

        }



        #endregion // Private methods



        #region Private fields



        /// <summary>

        ///   Visual Studio IDE.

        /// </summary>

        private _DTE m_applicationObject;



        /// <summary>

        ///   Addin instance.

        /// </summary>

        private AddIn m_addInInstance;



        /// <summary>

        ///   UI elements (i.e. commandbar controls)

        /// </summary>

        private UiElements m_uiElements;



        /// <summary>

        ///   Object that invokes NUnit.

        /// </summary>

        private NUnitExecutor m_nUnitExecutor;



        private static readonly string TextNUnitExeNotFound;

        private static readonly string TextInvalidConfiguration;

        private static readonly string TextApplicationNotConfigured;

        private static readonly string TextConfigurationSaved;



        #endregion // Private fields

    }

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The zlib/libpng License


Written By
Software Developer (Senior)
Croatia Croatia
Graduated at the Faculty of Electrical Engineering and Computing, University of Zagreb (Croatia) and received M.Sc. degree in electronics. For several years he was research and lecturing assistant in the fields of solid state electronics and electronic circuits, published several scientific and professional papers, as well as a book "Physics of Semiconductor Devices - Solved Problems with Theory" (in Croatian).
During that work he gained interest in C++ programming language and have co-written "C++ Demystified" (in Croatian), 1st edition published in 1997, 2nd in 2001, 3rd in 2010, 4th in 2014.
After book publication, completely switched to software development, programming mostly in C++ and in C#.
In 2016 coauthored the book "Python for Curious" (in Croatian).

Comments and Discussions