Click here to Skip to main content
15,892,005 members
Articles / Web Development / ASP.NET

MbUnit : Generative Unit Test Framework

Rate me:
Please Sign up or sign in to vote.
4.70/5 (38 votes)
15 Apr 20046 min read 221.9K   496   120  
A new highly flexible unit test framework with new fixtures
using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Windows.Forms;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Drawing.Imaging;
using System.Drawing;
using System.IO;

namespace GUnit.Forms
{
	using GUnit.Core;
	using GUnit.Core.Collections;
	using GUnit.Core.Framework;
	using GUnit.Core.Invokers;

	/// <summary>
	/// Summary description for ReflectorTreeView.
	/// </summary>
	public class ReflectorTreeView : System.Windows.Forms.UserControl, IMessageFilter
	{
		private AssemblyCollection assemblies = new AssemblyCollection();
		private Hashtable assemblyNodes = new Hashtable();
		private Hashtable fixtureNodes  = new Hashtable();
		private Hashtable nodeFixtures = new Hashtable();
		private Hashtable namespaceNodes = new Hashtable();
		private ReflectionImageList reflectionImageList = null;

		private System.Windows.Forms.TreeView typeTree;
		private System.Windows.Forms.ImageList treeImageList;
		private System.ComponentModel.IContainer components;


		public ReflectorTreeView()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();


			Application.AddMessageFilter(this);
			DragAcceptFiles(this.Handle, true);

			this.reflectionImageList = new ReflectionImageList(this.treeImageList);
		}

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		public TreeView TypeTree
		{
			get
			{
				return this.typeTree;
			}
		}

		public TreeNodeCollection Nodes
		{
			get
			{
				return this.typeTree.Nodes;
			}
		}

		public event TreeViewEventHandler AfterSelect;

		public event EventHandler TreeCleared;

		protected void OnTreeCleared()
		{
			if (this.TreeCleared!=null)
				TreeCleared(this, new EventArgs());
		}

		public event EventHandler TreePopulated;

		protected void OnTreePopulated()
		{
			if (this.TreePopulated!=null)
				TreePopulated(this, new EventArgs());
		}

		public event RunPipeSuccessEventHandler TestSuccess;
		public event RunPipeFailureEventHandler TestFailure;

		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			this.typeTree = new System.Windows.Forms.TreeView();
			this.treeImageList = new System.Windows.Forms.ImageList(this.components);
			this.SuspendLayout();
			// 
			// typeTree
			// 
			this.typeTree.Dock = System.Windows.Forms.DockStyle.Fill;
			this.typeTree.ImageList = this.treeImageList;
			this.typeTree.Location = new System.Drawing.Point(0, 0);
			this.typeTree.Name = "typeTree";
			this.typeTree.Size = new System.Drawing.Size(392, 256);
			this.typeTree.TabIndex = 0;
			this.typeTree.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.typeTree_AfterSelect);
			// 
			// treeImageList
			// 
			this.treeImageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
			this.treeImageList.ImageSize = new System.Drawing.Size(16, 16);
			this.treeImageList.TransparentColor = System.Drawing.Color.Transparent;
			// 
			// ReflectorTreeView
			// 
			this.Controls.Add(this.typeTree);
			this.Name = "ReflectorTreeView";
			this.Size = new System.Drawing.Size(392, 256);
			this.ResumeLayout(false);

		}
		#endregion


		#region Drag & Drop support

		[DllImport("shell32.dll")]
		private static extern int DragQueryFile(IntPtr hdrop, int ifile, StringBuilder fname, int fnsize);  
		[DllImport("shell32.dll")]
		private static extern int DragAcceptFiles(IntPtr hwnd, bool accept);
		[DllImport("shell32.dll")]
		private static extern void DragFinish(IntPtr hdrop); 
		private const int WM_DROPFILES = 563; 

		public bool PreFilterMessage(ref Message m)
		{
			if (m.Msg == WM_DROPFILES)
			{
				int nFiles = DragQueryFile(m.WParam,-1,null,0);
				for(int i=0;i<nFiles;++i)
				{
					StringBuilder sb = new StringBuilder(256);
					DragQueryFile(m.WParam, i, sb, 256);
					HandleDroppedFiles(sb.ToString());
				}
				DragFinish(m.WParam);
				PopulateTree();

				return true;
			}
			return false;
		}

		private void HandleDroppedFiles(string file)
		{
			if (file!=null && file.Length>0)
				LoadAssembly(file);

		}
		#endregion

		#region Assembly handling
		protected AssemblyCollection Assemblies
		{
			get
			{
				return this.assemblies;
			}
		}

		public void LoadAssembly(string file)
		{
			try
			{
				Assembly a = Assembly.LoadFile(file);
				if (this.assemblies.Contains(a))
					return;

				AddAssembly(a);
			}
			catch(Exception)
			{}
		}

		public void AddAssembly(Assembly a)
		{
			if (a==null)
				throw new ArgumentNullException("a");

			if (!this.assemblies.Contains(a))
				this.assemblies.Add(a);
		}

		public void RemoveAssembly(Assembly a)
		{
			this.assemblies.Remove(a);
		}

		#endregion

		#region Tree population
		public void ClearTree()
		{
			this.typeTree.Nodes.Clear();
			this.assemblyNodes.Clear();
			this.fixtureNodes.Clear();
			this.nodeFixtures.Clear();
			this.namespaceNodes.Clear();

			OnTreeCleared();
		}

		public void PopulateTree()
		{
			this.ClearTree();

			ReflectionExplorer explo = new ReflectionExplorer(
				typeof(TestFixturePatternAttribute));

			explo.Explore(this.assemblies);

			foreach(DictionaryEntry de in explo.AssemblyTypes)
			{
				AddAssemblyNode((Assembly)de.Key);
				foreach(Type t in (TypeCollection)de.Value)
					AddTypeNode((Type)t);
			}

			OnTreePopulated();
		}

		private TreeNode GetNamespaceNode(Type t)
		{
			if (t==null)
				throw new ArgumentNullException("t");

			// get assembly
			if (!this.assemblyNodes.Contains(t.Assembly))
				throw new Exception("Assembly not found");

			TreeNode parent = (TreeNode)this.assemblyNodes[t.Assembly];
			string ns = "";
			foreach(string name in t.Namespace.Split('.'))
			{
				if (ns.Length==0)
					ns+=name;
				else
					ns+="."+name;

				if (!this.namespaceNodes.Contains(ns))
				{
					TestTreeNode  node = new TestTreeNode(name,TestNodeType.Namespace);

					this.namespaceNodes.Add(ns,node);
					parent.Nodes.Add(node);
					parent = node;
				}
				else
					parent = (TreeNode)this.namespaceNodes[ns];
			}

			return parent;
		}

		private void AddAssemblyNode(Assembly a)
		{
			TestTreeNode node = new TestTreeNode(
				a.GetName().Name,TestNodeType.Assembly);
			node.Tag = a;

			this.assemblyNodes.Add(a,node);
			this.typeTree.Nodes.Add(node);
		}

		private void AddTypeNode(Type t)
		{
			TreeNode parent = GetNamespaceNode(t);
			TestTreeNode node = new TestTreeNode(
				t.Name,TestNodeType.Fixture);

			this.fixtureNodes.Add(t,node);
			this.nodeFixtures[node]=t;
			parent.Nodes.Add(node);	

			RunInvokerTree tree = new RunInvokerTree(t);	
			AddFixtureNodes(node, tree);
		}

		private void AddFixtureNodes(TreeNode parent, RunInvokerTree tree)
		{
			// add sub fixtures
			foreach(RunPipe pipe in tree.AllTestPipes())
			{
				TestTreeNode pipeNode = new TestTreeNode(
					pipe.ShortName,TestNodeType.Test);
				RunPipeStarter starter = new RunPipeStarter(pipe);
				pipeNode.Tag = starter;

				RunTestObserver obPipe = new RunTestObserver(pipeNode);
				starter.Start +=new RunPipeEventHandler(obPipe.Start);
				//starter.Snap +=new RunSuccessEventHandler(obPipe.Snap);
				starter.Success +=new RunPipeSuccessEventHandler(obPipe.Success);
				starter.Success += this.TestSuccess;
				starter.Failure +=new RunPipeFailureEventHandler(obPipe.Failure);
				starter.Failure += this.TestFailure;
				parent.Nodes.Add(pipeNode);

				foreach (RunInvokerVertex v in pipe.Invokers)
				{
					TestTreeNode invokerNode = new TestTreeNode(
						v.Invoker.Name,TestNodeType.Invoker);
					invokerNode.Tag = v.Invoker;
					pipeNode.Nodes.Add(invokerNode);

					RunTestObserver obInvoker = new RunTestObserver(invokerNode);
					//starter.Start +=new RunPipeEventHandler(obPipe.Start);
					starter.Snap +=new RunSuccessEventHandler(obInvoker.Snap);
					//starter.Success +=new RunPipeSuccessEventHandler(obPipe.Success);
					//starter.Failure +=new RunPipeFailureEventHandler(obPipe.Failure);
				}
			}
		}

		public Type Fixture(TreeNode node)
		{
			if (node==null)
				return null;
			return (Type)this.nodeFixtures[node];
		}

		#endregion

		public void RunTests()
		{
			if (this.typeTree.SelectedNode==null)
			{
				this.typeTree.CollapseAll();
				foreach(TestTreeNode node in this.typeTree.Nodes)
				{
					RunTests(node);
				}
			}
			else
				RunTests((TestTreeNode)this.typeTree.SelectedNode);
		}


		private void typeTree_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
		{
			if (this.AfterSelect!=null)
				AfterSelect(this,e);
		}

		public void RunTests(TestTreeNode node)
		{
			if (node.Tag is RunPipeStarter)
			{
				node.State = TestState.NotRun;
				RunPipeStarter.Launch((RunPipeStarter)node.Tag,true);
			}
			foreach(TestTreeNode child in node.Nodes)
			{
				RunTests(child);
			}
			this.typeTree.Invalidate();
		}

		public int TestCount
		{
			get
			{
				return TestChildCount(this.typeTree.Nodes);
			}
		}

		internal int TestChildCount(TreeNodeCollection childs)
		{
			int count = 0;
			foreach(TreeNode child in childs)
			{
				if (child.Tag is RunPipeStarter)
					++count;
				else
					count += TestChildCount(child.Nodes);
			}
			return count;
		}
	}
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Engineer
United States United States
Jonathan de Halleux is Civil Engineer in Applied Mathematics. He finished his PhD in 2004 in the rainy country of Belgium. After 2 years in the Common Language Runtime (i.e. .net), he is now working at Microsoft Research on Pex (http://research.microsoft.com/pex).

Comments and Discussions