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

How to quickly debug a NUnit test in Visual Studio

Rate me:
Please Sign up or sign in to vote.
4.67/5 (6 votes)
30 Sep 2009CPOL5 min read 46.1K   445   21  
An article on quickly debugging NUnit tests.
//-------------------------------------------------------------------------------------
// <copyright file='VSCodeFile.cs' company='Jonno'>
//     Copyright (c) Paul Johnson 2009 (email:paulmichael.johnson@gmail.com)
//     Code is released under The Code Project Open License (CPOL).
// </copyright>
//-------------------------------------------------------------------------------------

namespace Jonno.AddIns.Entities
{
    using System;        
    using EnvDTE;
    using EnvDTE80;
    using Jonno.Extensions;
    
    /// <summary>
    /// A Visual Studio code file.
    /// </summary>
    internal class VSCodeFile : BaseCodeFile
    {
        public VSCodeFile(Project project, ProjectItem item)
        {
            this.Project = project;

            this.SetCurrentItemAndGetInformation(item);

            this.IsActive = (item == item.DTE.ActiveWindow.ProjectItem ? true : false);
        }

        private ProjectItem CurrentItem { get; set; }

        private TextDocument Document { get; set; }

        public override void FindText(string text, bool moveToEol)
        {
            this.Document.Selection.StartOfDocument(false);

            if (string.IsNullOrEmpty(text))
            {
                return;
            }

            var result = this.Document.Selection.FindText(text, (int)vsFindOptions.vsFindOptionsNone);

            if (result && moveToEol)
            {
                this.Document.Selection.EndOfLine(false);
            }
        }

        public override CompositeCodeElement GetCodeElementAtSelection()
        {
            var current = this.Document.Selection.ActivePoint.AbsoluteCharOffset;
            var elements = this.Elements.Where(
                element => 
                element.StartAbsoluteCharacterOffset <= current &&
                element.EndAbsoluteCharacterOffset >= current);

            return elements.FindLastElement();            
        }

        public override string GetUniqueTextFromSelectionPoint()
        {
            var sp = this.Document.Selection.ActivePoint.CreateEditPoint();

            var ep = sp.CreateEditPoint();
            var ep2 = sp.CreateEditPoint();

            ep.StartOfLine();
            ep2.EndOfLine();

            var currentSelectionText = this.GetSelectionBetweenEditPoints(ep, ep2);

            if (this.Text.CountOccurances(currentSelectionText) == 1)
            {
                return currentSelectionText;
            }

            bool goUp = true;

            if (ep.AtStartOfDocument)
            {
                goUp = false;
            }

            do
            {
                if (goUp)
                {
                    ep.LineUp(1);
                    ep.StartOfLine();
                }
                else
                {
                    ep2.LineDown(1);
                    ep2.EndOfLine();
                }

                currentSelectionText = this.GetSelectionBetweenEditPoints(ep, ep2);
            }
            while (this.Text.CountOccurances(currentSelectionText) > 1);

            return currentSelectionText;
        }

        public override void ReplaceText(string find, string with)
        {
            this.DoSomethingAndResetActivePoint(() =>
                {
                    var options = (int)vsFindOptions.vsFindOptionsMatchCase;

                    this.Document.ReplaceText(find, with, options);
                });

            this.FormatChangedText();
        }

        public override void ReplaceAllText(string text)
        {
            if (this.Text == text)
            {
                return;
            }

            var unique = this.GetUniqueTextFromSelectionPoint();

            var editPoint = this.Document.StartPoint.CreateEditPoint();

            editPoint.ReplaceText(
                this.Document.EndPoint,
                text,
                (int)vsEPReplaceTextOptions.vsEPReplaceTextAutoformat);

            this.Text = text;

            this.FormatChangedText();

            this.FindText(unique, true);
        }

        public override void HighlightText(int position, int length)
        {
            try
            {
                this.Document.Selection.MoveToAbsoluteOffset(position, false);
                this.Document.Selection.MoveToAbsoluteOffset(position + length, true);
            }
            catch
            {
            }

            this.SetSelection();
        }

        public override void SwitchToInDocument()
        {
            var current = this.CurrentItem.DTE.ActiveWindow;
            var wanted = this.CurrentItem.DTE.Windows.Item(this.ShortName);

            if (current != wanted)
            {
                wanted.Activate();
            }
        }

        protected override void RefreshElements()
        {
            this.Elements = new CompositeCodeElements();

            if (this.CurrentItem.FileCodeModel == null)
            {
                return;
            }

            var elements = this.CurrentItem.FileCodeModel.CodeElements;

            foreach (CodeElement item in elements)
            {
                this.IterateCodeElement(item, null);
            }
        }
        
        protected override void GetText()
        {
            this.Document = this.GetTextDocumentFromProjectItem();

            var editPoint = this.Document.StartPoint.CreateEditPoint();
            this.Text = editPoint.GetText(this.Document.EndPoint);

            this.SetSelection();
        }
        
        protected override void GetFileName()
        {
            this.FileName = this.CurrentItem.get_FileNames(0);
        }
        
        private void FormatChangedText()
        {
            this.Document.DTE.ExecuteCommand("Edit.FormatDocument", string.Empty);

            this.GetText();

            this.RefreshElements();
        }

        private void SetSelection()
        {
            this.CurrentSelection = this.Document.Selection.Text;
            this.CurrentSelectionPosition = this.Document.Selection.ActivePoint.AbsoluteCharOffset;
        }

        private void DoSomethingAndResetActivePoint(Action action)
        {
            var previousPoint = this.Document.Selection.ActivePoint.CreateEditPoint();

            action();

            try
            {
                this.Document.Selection.MoveToPoint(previousPoint, false);
            }
            catch (System.Exception)
            {
                this.Document.Selection.MoveToPoint(this.Document.StartPoint, false);
            }
        }

        private void SetCurrentItemAndGetInformation(ProjectItem item)
        {
            this.CurrentItem = item;
            this.RefreshInformation();
        }

        private TextDocument GetTextDocumentFromProjectItem()
        {
            // Open the file as a source code file
            if (!this.CurrentItem.get_IsOpen(Constants.vsViewKindAny))
            {
                return this.CurrentItem.Open(Constants.vsViewKindCode).Document.Object("TextDocument") as TextDocument;                
            }
            
            return this.CurrentItem.Document.Object("TextDocument") as TextDocument;	        
        }

        private void IterateCodeElement(CodeElement element, CompositeCodeElement elementParent)
        {
            bool doChildren = false;

            // set the defaults 
            var add = false;
            var access = vsCMAccess.vsCMAccessDefault;
            var elementType = ElementType.Unknown;

            var comment = string.Empty;
            var docComment = string.Empty;
            var name = string.Empty;
            var fullName = string.Empty;
            var parentId = string.Empty;
            var isShared = false;
            var isConstant = false;

            // get what we can from the CodeElement type
            var text = element.StartPoint.CreateEditPoint().GetText(element.EndPoint);
            var startPoint = element.StartPoint.AbsoluteCharOffset;
            var endPoint = element.EndPoint.AbsoluteCharOffset;
            var newId = System.Guid.NewGuid().ToString();

            if (elementParent != null)
            {
                parentId = elementParent.Id;
            }

            switch (element.Kind)
            {
                case vsCMElement.vsCMElementAttribute:
                    {
                        elementType = ElementType.Attribute;
                        add = true;

                        var casted = element as CodeAttribute;
                        name = casted.Name;
                        fullName = casted.FullName;
                    }

                    break;

                case vsCMElement.vsCMElementClass:
                    {
                        elementType = ElementType.Class;
                        add = true;
                        doChildren = true;

                        var casted = element as CodeClass;
                        access = casted.Access;
                        docComment = casted.DocComment;
                        comment = casted.Comment;
                        name = casted.Name;
                        fullName = casted.FullName;
                    }

                    break;

                case vsCMElement.vsCMElementDelegate:
                    {
                        elementType = ElementType.Delegate;
                        add = true;

                        var casted = element as CodeDelegate;
                        access = casted.Access;
                        docComment = casted.DocComment;
                        comment = casted.Comment;
                        name = casted.Name;
                        fullName = casted.FullName;
                    }

                    break;

                case vsCMElement.vsCMElementEnum:
                    {
                        elementType = ElementType.Enum;
                        add = true;

                        var casted = element as CodeEnum;
                        access = casted.Access;
                        docComment = casted.DocComment;
                        comment = casted.Comment;
                        name = casted.Name;
                        fullName = casted.FullName;
                    }

                    break;

                case vsCMElement.vsCMElementEvent:
                    {
                        elementType = ElementType.Event;
                        add = true;

                        var casted = element as CodeEvent;
                        access = casted.Access;
                        docComment = casted.DocComment;
                        comment = casted.Comment;
                        name = casted.Name;
                        fullName = casted.FullName;
                        isShared = casted.IsShared;
                    }

                    break;

                case vsCMElement.vsCMElementFunction:
                    {
                        elementType = ElementType.Method;
                        add = true;
                        doChildren = true;

                        var casted = element as CodeFunction;
                        access = casted.Access;
                        docComment = casted.DocComment;
                        comment = casted.Comment;
                        name = casted.Name;
                        fullName = casted.FullName;
                        isShared = casted.IsShared;

                        if (casted.Parent != null && casted.Parent is CodeElement)
                        {
                            var parent = casted.Parent as CodeElement;
                            var parentName = parent.Name;

                            if (name == parentName)
                            {
                                elementType = ElementType.Constructor;

                                if (isShared)
                                {
                                    access = vsCMAccess.vsCMAccessPublic;
                                }
                            }
                        }
                    }

                    break;

                case vsCMElement.vsCMElementImportStmt:
                    {
                        elementType = ElementType.Import;
                        add = true;
                    }

                    break;

                case vsCMElement.vsCMElementInterface:
                    {
                        elementType = ElementType.Interface;
                        add = true;
                        doChildren = true;

                        var casted = element as CodeInterface;
                        access = casted.Access;
                        docComment = casted.DocComment;
                        comment = casted.Comment;
                        name = casted.Name;
                        fullName = casted.FullName;
                    }

                    break;

                case vsCMElement.vsCMElementNamespace:
                    {
                        elementType = ElementType.Namespace;
                        add = true;
                        doChildren = true;
                    }

                    break;

                case vsCMElement.vsCMElementParameter:
                    {
                        elementType = ElementType.Parameter;
                        add = true;

                        var casted = element as CodeParameter;

                        docComment = casted.DocComment;
                        name = casted.Name;
                        fullName = casted.FullName;
                    }

                    break;

                case vsCMElement.vsCMElementProperty:
                    {
                        elementType = ElementType.Property;
                        add = true;

                        var casted = element as CodeProperty;
                        access = casted.Access;
                        docComment = casted.DocComment;
                        comment = casted.Comment;
                        name = casted.Name;
                        fullName = casted.FullName;
                        isShared = text.Contains(" static ");
                    }

                    break;

                case vsCMElement.vsCMElementStruct:
                    {
                        elementType = ElementType.Structure;
                        add = true;

                        var casted = element as CodeStruct;
                        access = casted.Access;
                        docComment = casted.DocComment;
                        comment = casted.Comment;
                        name = casted.Name;
                        fullName = casted.FullName;
                    }

                    break;

                case vsCMElement.vsCMElementVariable:
                    {
                        elementType = ElementType.Field;
                        add = true;

                        var casted = element as CodeVariable;
                        access = casted.Access;
                        docComment = casted.DocComment;
                        comment = casted.Comment;
                        name = casted.Name;
                        fullName = casted.FullName;
                        isShared = casted.IsShared;
                        isConstant = casted.IsConstant;
                    }

                    break;

                default:
                    break;
            }

            if (!add)
            {
                return;
            }

            var newElement = new CompositeCodeElement()
            {
                Id = newId,
                Name = name,
                FullName = fullName,
                Access = this.ConvertVisualStudioCMAccessType(access),
                Kind = elementType,
                StartAbsoluteCharacterOffset = startPoint,
                EndAbsoluteCharacterOffset = endPoint,
                Text = text,
                Comment = comment,
                DocComment = docComment,
                IsShared = isShared,
                IsConstant = isConstant,
                ParentId = parentId
            };

            this.Elements.Add(newElement);

            if (elementType == ElementType.Attribute && elementParent != null)            
            {
                elementParent.AddAttribute(text);
            }

            if (!doChildren)
            {
                return;
            }

            foreach (CodeElement item in element.Children)
            {
                this.IterateCodeElement(item, newElement);
            }
        }

        private string GetSelectionBetweenEditPoints(EditPoint ep1, EditPoint ep2)
        {
            return ep1.GetText(ep2).Trim();
        }

        private AccessType ConvertVisualStudioCMAccessType(vsCMAccess access)
        {
            switch (access)
            {
                case vsCMAccess.vsCMAccessProject:
                case vsCMAccess.vsCMAccessAssemblyOrFamily:
                    return AccessType.Internal;

                case vsCMAccess.vsCMAccessPrivate:
                    return AccessType.Private;

                case vsCMAccess.vsCMAccessProtected:
                    return AccessType.Protected;

                case vsCMAccess.vsCMAccessPublic:
                    return AccessType.Public;

                default:
                    return AccessType.NotApplicable;
            }
        }
    }
}

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 Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I have over 15 years of development experience, in many different languages, programming styles and platforms. Currently working as a C# coder, and residing in north Herts in the UK. I love lean software development and anything that reduces a grind to leave more time for useful coding!

Comments and Discussions