<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" gd:etag="W/&quot;DU4NRHw-fSp7ImA9WhNRGEw.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414</id><updated>2012-11-13T06:46:35.255-08:00</updated><category term="silly" /><category term="watin" /><category term="documentation" /><category term="gallio" /><category term="books" /><category term="recruiters" /><category term="service broker" /><category term="aop" /><category term="information" /><category term="low-level" /><category term="networking" /><category term="flex" /><category term="mvc" /><category term="release notes" /><category term="ui" /><category term="values" /><category term="jobs" /><category term="bio" /><category term="flexbridge" /><category term="rad" /><category term="software engineering" /><category term="languages" /><category term="typemock" /><category term="testing" /><category term="architecture" /><category term="learning" /><category term="rhino.mocks" /><category term="mbunit" /><category term="management" /><category term="ioc" /><title>Bits in Motion</title><subtitle type="html">Much Ado About Code</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>146</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/BitsInMotion" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="bitsinmotion" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;D0MBQ3w9eyp7ImA9WxFQFEo.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-3771511261435553163</id><published>2010-05-10T00:10:00.001-07:00</published><updated>2010-05-10T00:10:52.263-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-10T00:10:52.263-07:00</app:edited><title>Gallio v3.2 Beta</title><content type="html">&lt;p&gt;A couple weeks ago I posted that the new beta with R# 5.0 and VSTS 2010 support would be available.&amp;#160; Unfortunately I forgot to go back and post an update with a link.&lt;/p&gt;  &lt;p&gt;Here’s the link:&amp;#160; &lt;a href="http://ccnet.gallio.org/Distributables/"&gt;http://ccnet.gallio.org/Distributables/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Interim release notes: &lt;a title="http://www.gallio.org/wiki/doku.php?id=release_notes" href="http://www.gallio.org/wiki/doku.php?id=release_notes"&gt;http://www.gallio.org/wiki/doku.php?id=release_notes&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Happy testing!&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/8SMlgd-KNcQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/3771511261435553163/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=3771511261435553163" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/3771511261435553163?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/3771511261435553163?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2010/05/gallio-v32-beta.html" title="Gallio v3.2 Beta" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;A0QARnY_fSp7ImA9WxFSEks.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-5197841886280376694</id><published>2010-04-14T11:22:00.001-07:00</published><updated>2010-04-14T11:22:27.845-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-14T11:22:27.845-07:00</app:edited><title>Gallio v3.2 Beta Later this Week</title><content type="html">&lt;p&gt;This is just a short note to say that we will be releasing Gallio v3.2 Beta later this week with support for Visual Studio 2010 RTM and ReSharper 5.0.&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/urZ5WsCEjO0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/5197841886280376694/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=5197841886280376694" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5197841886280376694?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5197841886280376694?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2010/04/gallio-v32-beta-later-this-week.html" title="Gallio v3.2 Beta Later this Week" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;D0UEQ3c6cCp7ImA9WxBaE04.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-6336102035443606154</id><published>2010-03-23T01:28:00.001-07:00</published><updated>2010-03-23T02:06:42.918-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-23T02:06:42.918-07:00</app:edited><title>Sneaky Equality Bugs</title><content type="html">&lt;p&gt;While trying to figure out why running a single test with Gallio causes ReSharper 5.0 to throw up the test failure in a message box (which it never did it before), I found this sneaky little bug.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;public abstract class RemoteTask : IEquatable&amp;lt;RemoteTask&amp;gt;     &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public string RunnerId { get; set; } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; public override bool Equals(RemoteTask other)     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return Equals(other as RemoteTask);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; public bool Equals(RemoteTask other)     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return other != null &amp;amp;&amp;amp; RunnerId == other.RunnerId;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; public override int GetHashCode()     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return RunnerId.GetHashCode();      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Can you spot the bug?&lt;/p&gt;  &lt;h4&gt;Magic Triad: Dictionary&amp;lt;TKey, TValue&amp;gt;, EqualityComparer&amp;lt;T&amp;gt; and IEquatable&amp;lt;T&amp;gt;&lt;/h4&gt;  &lt;p&gt;Whenever we construct a &lt;em&gt;Dictionary&amp;lt;TKey, TValue&lt;/em&gt;&amp;gt; without providing an explicit &lt;em&gt;IEqualityComparer&amp;lt;TKey&amp;gt;&lt;/em&gt; in the constructor (which is most of the time), the .Net framework automatically provides one for us.&amp;#160; Namely, it uses &lt;em&gt;EqualityComparer&amp;lt;TKey&amp;gt;.Default&lt;/em&gt;.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;EqualityComparer&amp;lt;T&amp;gt;&lt;/em&gt; is one of those really useful generic classes that is really easy to overlook.&amp;#160; Its job is to choose an appropriate implementation of &lt;em&gt;IEqualityComparer&amp;lt;T&amp;gt;&lt;/em&gt; for any given type &lt;em&gt;T&lt;/em&gt;.&amp;#160; Here are the cases it considers.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;If &lt;em&gt;T&lt;/em&gt; is type &lt;em&gt;byte, &lt;/em&gt;then use a &lt;em&gt;ByteEqualityComparer&lt;/em&gt; that does the obvious byte comparison.&lt;/li&gt;    &lt;li&gt;If &lt;em&gt;T &lt;/em&gt;is assignable to type &lt;em&gt;IEquatable&amp;lt;T&amp;gt;,&lt;/em&gt; then use a &lt;em&gt;GenericEqualityComparer&amp;lt;T&amp;gt;&lt;/em&gt; that will call &lt;em&gt;IEquatable&amp;lt;T&amp;gt;.Equals&lt;/em&gt; method to determine equality.&lt;/li&gt;    &lt;li&gt;If &lt;em&gt;T &lt;/em&gt;is nullable and its value type &lt;em&gt;TV &lt;/em&gt;is assignable to type &lt;em&gt;IEquatable&amp;lt;TV&amp;gt;&lt;/em&gt;, then use a &lt;em&gt;NullableEqualityComparer&amp;lt;T&amp;gt;&lt;/em&gt; that performs a null-check then calls &lt;em&gt;IEquatable&amp;lt;TV&amp;gt;.Equals(TV)&lt;/em&gt; method to determine equality of non-null values.&lt;/li&gt;    &lt;li&gt;If &lt;em&gt;T &lt;/em&gt;is an enum whose underlying type is &lt;em&gt;int&lt;/em&gt;, then use an &lt;em&gt;EnumEqualityComparer&amp;lt;T&amp;gt;&lt;/em&gt; that compares enum values as integers.&lt;/li&gt;    &lt;li&gt;Otherwise, use an &lt;em&gt;ObjectEqualityComparer&amp;lt;T&amp;gt;&lt;/em&gt; that performs a null-check then compares objects using &lt;em&gt;Object.Equals&lt;/em&gt;.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;All of this magic is designed to do one thing: minimize the number of boxing conversions and casts required to compare objects.&lt;/p&gt;  &lt;h4&gt;The Bug&lt;/h4&gt;  &lt;p&gt;Did you notice that &lt;em&gt;RemoteTask&lt;/em&gt; is abstract?&amp;#160; That tells you that it is intended to be subclassed.&lt;/p&gt;  &lt;p&gt;To ensure correct equality and hash code semantics, a subclass must override at least the following two methods:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;bool IEquatable&amp;lt;RemoteTask&amp;gt;.Equals(RemoteTask)&lt;/li&gt;    &lt;li&gt;int Object.GetHashCode()&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;(Note: It is not necessary to override o&lt;em&gt;bject.Equals(object)&lt;/em&gt; because all it does is delegate to &lt;em&gt;IEquatable&amp;lt;RemoteTask&amp;gt;.Equals(RemoteTask)&lt;/em&gt;.&amp;#160; Due to covariance of the equality relation, this code will work fine for all subclasses as long as they override &lt;em&gt;IEquatable&amp;lt;RemoteTask&amp;gt;.Equals(RemoteTask)&lt;/em&gt; with their extra logic.)&lt;/p&gt;  &lt;p&gt;Unfortunately RemoteTask’s implementation of &lt;em&gt;IEquatable&amp;lt;RemoteTask&amp;gt;.Equals(RemoteTask)&lt;/em&gt; is not virtual!&amp;#160; Consequently subclasses cannot override it.&amp;#160; No matter what they do, they are stuck with an implementation that only considers the &lt;em&gt;RunnerId&lt;/em&gt; field when determining equality.&lt;/p&gt;  &lt;p&gt;As a matter of fact, all subclasses of &lt;em&gt;RemoteTask&lt;/em&gt; override &lt;em&gt;object.Equals(object)&lt;/em&gt; but not &lt;em&gt;IEquatable&amp;lt;RemoteTask&amp;gt;.Equals(RemoteTask). &lt;/em&gt; That’s really bad because the methods will probably return different results!&amp;#160; Any code that uses &lt;em&gt;IEquatable&amp;lt;RemoteTask&amp;gt;&lt;/em&gt; to compare &lt;em&gt;RemoteTask&lt;/em&gt; instances will be broken.&lt;/p&gt;  &lt;p&gt;This bug has been in ReSharper for a few releases now but it only just reared its ugly head due to an internal refactoring.&lt;/p&gt;  &lt;h5&gt;Previous Usage of RemoteTask.Equals (Hides the Bug)&lt;/h5&gt;  &lt;p&gt;Previously, ReSharper performed a lookup of tasks using linear search, something like this:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;private List&amp;lt;KeyValuePair&amp;lt;RemoteTask, UnitTestElement&amp;gt; tasks;     &lt;br /&gt;      &lt;br /&gt;public UnitTestElement GetTestElement(RemoteTask task)      &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; foreach (RemoteTask candidate in tasks)      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (object.Equals(task, candidate.Key))      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return candidate.Value;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; return null;      &lt;br /&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Notice that this code calls the &lt;em&gt;object.Equals(object, object)&lt;/em&gt; static helper function which performs a null check then determines equality using &lt;em&gt;object.Equals(object). &lt;/em&gt; In this case, subclasses that just override &lt;em&gt;object.Equals(object)&lt;/em&gt; will be fine.&lt;/p&gt;  &lt;h5&gt;Refactored Usage of RemoteTask.Equals (Triggers the Bug)&lt;/h5&gt;  &lt;p&gt;For efficiency, ReSharper now performs a lookup of tasks using a hash table, something like this:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;private Dictionary&amp;lt;RemoteTask, UnitTestElement&amp;gt; tasks;     &lt;br /&gt;      &lt;br /&gt;public UnitTestElement GetTestElement(RemoteTask task)      &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; UnitTestElement element;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; tasks.TryGetValue(task, out element);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; return element;      &lt;br /&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;What’s new here is that under the hood, the &lt;em&gt;Dictionary&amp;lt;RemoteTask, UnitTestElement&amp;gt;&lt;/em&gt; uses &lt;em&gt;EqualityComparer&amp;lt;RemoteTask&amp;gt;.Default&lt;/em&gt;.&amp;#160; That in turn compares values using &lt;em&gt;IEquatable&amp;lt;RemoteTask&amp;gt;.Equals(RemoteTask)&lt;/em&gt;… &lt;/p&gt;  &lt;p&gt;Oh yeah, that’s the method that subclasses can’t override…&lt;/p&gt;  &lt;p&gt;The net result is that the hash table considers all values to be equal as long as they have the same &lt;em&gt;RunnerId&lt;/em&gt;, regardless of their type or whatever other properties they may consider in their implementation of &lt;em&gt;object.Equals(object).&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;For the most part, this is only a problem when there are hash code collisions.&amp;#160; If the hash codes are always distinct then it won’t matter to the hash table that the equality comparer it is using returns true more often than it should…&lt;/p&gt;  &lt;p&gt;In other words, the fact that this code appears to work at all today is a matter of pure luck given that hash code collisions are fairly rare (for well designed hash functions, at least).&lt;/p&gt;  &lt;h4&gt;Moral&lt;/h4&gt;  &lt;p&gt;If you implement &lt;em&gt;IEquatable&amp;lt;T&amp;gt;&lt;/em&gt; in a class that is intended to be subclasses, make sure to make it virtual!&amp;#160; Also make sure the subclasses override it as needed!&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/NBUrk8-bpPA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/6336102035443606154/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=6336102035443606154" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/6336102035443606154?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/6336102035443606154?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2010/03/sneaky-bugs.html" title="Sneaky Equality Bugs" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;A0cHR3oyeyp7ImA9WxBWGUg.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-3719933129561010047</id><published>2010-02-11T23:05:00.001-08:00</published><updated>2010-02-11T23:17:16.493-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-11T23:17:16.493-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Accessing Non-Public Members Using Mirrors</title><content type="html">&lt;p&gt;MbUnit v3.2 introduces a new feature called Mirrors for accessing non-public members.&lt;/p&gt;  &lt;p&gt;Please tell me what you think.&amp;#160; :-)&lt;/p&gt;  &lt;p&gt;More on the wiki: &lt;a href="http://www.gallio.org/wiki/doku.php?id=mbunit:mirror"&gt;Mirrors&lt;/a&gt;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:cd12792d-71fd-412f-b252-62d43a0a0be7" class="wlWriterEditableSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/qfohz8kWsz0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/3719933129561010047/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=3719933129561010047" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/3719933129561010047?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/3719933129561010047?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2010/02/accessing-non-public-members-using.html" title="Accessing Non-Public Members Using Mirrors" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;DkMEQ38-eSp7ImA9WxBWGUk.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-8470227546391059045</id><published>2010-02-11T18:55:00.001-08:00</published><updated>2010-02-11T19:13:22.151-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-11T19:13:22.151-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="software engineering" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Setting Up a Distributed Build – Part 1</title><content type="html">&lt;p&gt;Earlier today I wrote about the fact that I need to &lt;a href="http://blog.bits-in-motion.com/2010/02/gallio-build-server-down-for.html"&gt;rebuild the Gallio build server&lt;/a&gt;.&amp;#160; Then I went downstairs to get ready to leave for work and I promptly twisted my ankle.&lt;/p&gt;  &lt;p&gt;So… now&amp;#160; lying in bed with an ice pack strapped to my ankle, this seems like as good a time as any to start planning.&lt;/p&gt;  &lt;h4&gt;Requirements&lt;/h4&gt;  &lt;p&gt;Gallio currently ships with plugins for several 3rd party tools.&amp;#160; It supports the .Net CLR 2.0 and 4.0 on 32bit and 64bit Windows platforms and aims to eventually support Mono on Windows and on Linux.&amp;#160; All told there are quite a few configurations to be tested.&lt;/p&gt;  &lt;p&gt;Here’s what we need:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Distributed compilation: components are compiled on platforms that provide the required dependencies.&lt;/li&gt;    &lt;li&gt;Distributed testing: components are tested on platforms that reflect a significant subset of the supported configurations.&lt;/li&gt;    &lt;li&gt;Distributed packaging: groups of components need to be assembled into packages on platforms that support the required packaging tools.&lt;/li&gt;    &lt;li&gt;Fully automatic continuous integration.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Here are some nice things to have:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Centralized control of builds and maintenance procedures.&lt;/li&gt;    &lt;li&gt;Centralized reporting of test results, code coverage, performance metrics and defects.&lt;/li&gt;    &lt;li&gt;Centralized publication of all artifacts such as binaries and documentation.&lt;/li&gt;    &lt;li&gt;Hermetic build environment.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Observation:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Most components are binary portable and should only be &lt;em&gt;compiled once&lt;/em&gt; but may need to be &lt;em&gt;tested more than once&lt;/em&gt; depending on how the configuration of the environment affects the component.&lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;Hermetic Build Environment&lt;/h4&gt;  &lt;p&gt;Hermetic means “sealed.”&lt;/p&gt;  &lt;p&gt;A hermetic build environment is one which encapsulates all of its dependencies so that the result of the build is entirely reproducible.&lt;/p&gt;  &lt;p&gt;Think for a moment about the dependencies of your own build process.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Source code.&lt;/li&gt;    &lt;li&gt;3rd party libraries and tools.&lt;/li&gt;    &lt;li&gt;SDKs, compilers, frameworks, header files.&lt;/li&gt;    &lt;li&gt;Test tools, coverage tools, build automation tools, documentation tools, linting tools.&lt;/li&gt;    &lt;li&gt;Scripting languages, command-line tools, scripts.&lt;/li&gt;    &lt;li&gt;Configuration files, registry entries.&lt;/li&gt;    &lt;li&gt;Custom patches, tweaks.&lt;/li&gt;    &lt;li&gt;Operating System revision and patch level.&lt;/li&gt;    &lt;li&gt;Crazy glue.&lt;/li&gt;    &lt;li&gt;Position of the mouse pointer on the screen.&amp;#160; (Please don’t depend on this!)&lt;/li&gt;    &lt;li&gt;Time of day.&amp;#160; (Ditto.)&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;The more dependencies you control, the more reproducible will be the build.&lt;/p&gt;  &lt;p&gt;The idea of establishing a hermetic build environment is to ensure that the dependencies are tightly controlled.&amp;#160; If I change line 101 of Foo.cs to fix a bug, I need to know that the next build will be identical to the first except that it will contain my specific bug fix because that is the &lt;em&gt;only change in the source tree&lt;/em&gt;.&lt;/p&gt;  &lt;p&gt;Let’s suppose I upgraded the compiler in place.&amp;#160; Now it’s possible that due to some changes in the compiler, the same source code that I compiled yesterday might produce different output today.&amp;#160; That’s really bad.&amp;#160; It means that I could introduce new bugs otherwise unchanged versions of my code just by recompiling it!&lt;/p&gt;  &lt;p&gt;One solution is to indicate in the source code exactly which version of the compiler should be used.&amp;#160; That way if I recompile yesterday’s code, then I will use exactly the same version of the compiler that was originally specified yesterday.&amp;#160; The only way to introduce a change would be to commit a change in the source tree indicating that a new compiler version should be used instead.&lt;/p&gt;  &lt;p&gt;The configuration of a hermetic build environment is well-known and consists of fully specified dependencies.&amp;#160; The only way to change the output of the build is by changing something in the source.&lt;/p&gt;  &lt;p&gt;Crucially, the hermetic build environment should not be permanently altered by the build process itself.&amp;#160; For example, running tests shouldn’t leave extra temporary files floating around that remain for the next build to trip over.&amp;#160; The environment should be pristine!&lt;/p&gt;  &lt;p&gt;How do we implement something like this?&lt;/p&gt;  &lt;p&gt;In Linux, we might run the whole build inside of a very carefully constructed &lt;a href="http://en.wikipedia.org/wiki/Chroot"&gt;chroot&lt;/a&gt; directory assuming that we don’t care about the kernel version or that we control it via other means.&lt;/p&gt;  &lt;p&gt;In Windows, it’s a lot harder because important configuration information could in principle reside anywhere in the file system or system registry.&amp;#160; For example, what really happens when we install a new version of .Net?&amp;#160; Hmm…&amp;#160; A more practical concern for Gallio is what happens when we upgrade to a new ReSharper beta, for example.&lt;/p&gt;  &lt;p&gt;The case of the ReSharper dependency is interesting.&amp;#160; To compile Gallio’s ReSharper extensions, it would be sufficient to have all of the ReSharper libraries checked into the source tree.&amp;#160; However, to test those extensions we actually need a working installation of ReSharper, which itself requires Visual Studio, which itself requires many things.&amp;#160; Checking in libraries isn’t enough.&amp;#160; We’re going to need a virtual machine snapshot.&lt;/p&gt;  &lt;h4&gt;Gather-Scatter-Gather-…&lt;/h4&gt;  &lt;p&gt;A build process can be described as a sequence of &lt;em&gt;gathering&lt;/em&gt; and &lt;em&gt;scattering&lt;/em&gt; operations.&lt;/p&gt;  &lt;p&gt;Here’s a fairly straightforward example:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Task 1&lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;Gather: Check out all of the sources for the core components from the source tree for a specific reversion.&lt;/li&gt;      &lt;li&gt;Scatter: Compile.&lt;/li&gt;      &lt;li&gt;Gather: Copy the compiled binaries to a common directory.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;Task 2&lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;Gather: Grab the compiled binaries from the first task.&lt;/li&gt;      &lt;li&gt;Scatter: Compile extensions with complex environmental dependencies.&lt;/li&gt;      &lt;li&gt;Gather: Copy the compiled extensions to a common directory.&amp;#160; Generate the documentation and the installers.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;Task 3&lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;Gather: Grab the installer from the second task.&lt;/li&gt;      &lt;li&gt;Scatter: Install and test extensions with complex environmental dependencies.&lt;/li&gt;      &lt;li&gt;Gather: Copy all test results into a test database.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;Task 4&lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;Gather: Grab the installer and documentation from the second task.&lt;/li&gt;      &lt;li&gt;Scatter: Publish the build artifacts and documentation to the web.&lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;  &lt;p&gt;In general, tasks gather stuff from the source tree or from the output of other tasks, scatter a bunch of derived artifacts around, then eventually gather them together again into a form that can be consumed by subsequent tasks.&lt;/p&gt;  &lt;p&gt;We can represent the dependencies as a linear sequence of steps (as outlined above) except that we would be better off if we could take advantage of the inherent parallelism of certain stages.&amp;#160; For example, there’s no reason we couldn’t be compiling and testing the ReSharper 4.5 extension at the same time as the ReSharper 5.0 extension as long as both processes ran independently (in different virtual machines).&amp;#160; Instead we should model dependencies as a graph of parallel or sequential tasks.&lt;/p&gt;  &lt;p&gt;To make this work, we will need a continuous integration tool that coordinates task dependencies elegantly.&lt;/p&gt;  &lt;h4&gt;Outlining a Solution&lt;/h4&gt;  &lt;p&gt;Here’s what the solution will look like for us.&lt;/p&gt;  &lt;h5&gt;Virtual Hermetic Build Environment&lt;/h5&gt;  &lt;p&gt;Because we need to build and test many components on Windows, the most robust and perhaps simplest way to establish a hermetic build environment will be to use a suite of virtual machines.&lt;/p&gt;  &lt;p&gt;To that end, the virtual machine host will maintain a collection virtual machine snapshots.&amp;#160; Each snapshot will have a unique name, like &lt;em&gt;“Win7 x64, VS 2010, R# 5.0.1161.9 (Snapshot 1.0)”&lt;/em&gt;.&lt;/p&gt;  &lt;p&gt;There are many virtual machines that we could use, but for our purposes we will use &lt;a href="http://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt;.&amp;#160; VirtualBox is free and performs well although I have found it to be somewhat unstable at times.&amp;#160; Even so, it’s perfect for what we need.&lt;/p&gt;  &lt;p&gt;We’re going to have a &lt;strong&gt;lot&lt;/strong&gt; of snapshots.&amp;#160; Every time we tweak one of the virtual machines, we will create a new snapshot and then check in a change to the source tree to use the new snapshot for builds of subsequent revisions.&amp;#160; Fortunately VirtualBox supports &lt;a href="http://www.virtualbox.org/manual/UserManual.html#diffimages"&gt;differencing disk images&lt;/a&gt; and &lt;a href="http://www.virtualbox.org/manual/UserManual.html#snapshots"&gt;snapshot trees&lt;/a&gt;.&amp;#160; As a result, we only need to store the differences between snapshots.&amp;#160; What’s more, we can easily reset the virtual machine to a previous snapshot state just by throwing away the latest differencing disk image which is very fast.&lt;/p&gt;  &lt;p&gt;Over time, we may choose to throw away some snapshots when they are no longer relevant for practical purposes since there will come a time when we will never need to rebuild most old revisions.&amp;#160; In the meantime, gigabytes are cheap and plentiful, time is not!&lt;/p&gt;  &lt;p&gt;How many virtual machines will there be?&amp;#160; I’m not sure… there may be as many as a dozen.&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Windows Server 2003, continuous integration server, build agents and other infrastructure.&lt;/li&gt;    &lt;li&gt;Windows 7 x86, .Net Framework 2.0 &amp;amp; 4.0, Mono, NCover.&lt;/li&gt;    &lt;li&gt;Windows 7 x64, .Net Framework 2.0 &amp;amp; 4.0, Mono, NCover.&lt;/li&gt;    &lt;li&gt;Windows 7 x86, ReSharper 4.5, Visual Studio 2008, NCover.&lt;/li&gt;    &lt;li&gt;Windows 7 x86, ReSharper 5.0, Visual Studio 2010, NCover.&lt;/li&gt;    &lt;li&gt;Windows 7 x86, TypeMock, NCover.&lt;/li&gt;    &lt;li&gt;Windows 7 x86, AutoCAD 2010, NCover.&lt;/li&gt;    &lt;li&gt;Windows 7 x86, TeamCity, NCover.&amp;#160; (for testing TeamCity extensions)&lt;/li&gt;    &lt;li&gt;Windows 7 x86, CCNet, NCover.&amp;#160; (for testing CCNet extensions)&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.ubuntu.com/"&gt;Ubuntu&lt;/a&gt;, &lt;a href="http://mono-project.com/"&gt;Mono&lt;/a&gt;, &lt;a href="http://mono-project.com/Code_Coverage"&gt;MonoCov&lt;/a&gt;.&lt;/li&gt;    &lt;li&gt;… more… ?&lt;/li&gt; &lt;/ol&gt;  &lt;h5&gt;Continuous Integration&lt;/h5&gt;  &lt;p&gt;The build process will be broken down into a graph of tasks.&amp;#160; Each task will specify dependencies on other tasks whose output it consumes.&amp;#160; We’ll use a &lt;a href="http://en.wikipedia.org/wiki/Continuous_integration"&gt;continuous integration&lt;/a&gt; build manager to run the tasks and report progress.&lt;/p&gt;  &lt;p&gt;There are many different continuous integration tools that we could use, but for our purposes we will use &lt;a href="http://www.jetbrains.com/teamcity/index.html"&gt;TeamCity&lt;/a&gt;.&amp;#160; TeamCity does a very good job of managing dependencies and the professional edition is available for free and does everything we need.&lt;/p&gt;  &lt;p&gt;Interestingly, because we will be using virtual machine snapshots as part of our hermetic build environment, we will not use TeamCity’s built-in support for cross-platform builds.&lt;/p&gt;  &lt;p&gt;JetBrain’s recommended approach for cross-platform builds is to run multiple TeamCity build agents on multiple machines with different configurations.&amp;#160; However for that to work, the machine has to persist the state of the built agent.&amp;#160; The problem is that we will always wipe our virtual machines back to their snapshot state after each build.&amp;#160; So if the agents ran inside the virtual machine they would lose their state changes completely between builds which would probably make TeamCity somewhat unhappy.&lt;/p&gt;  &lt;p&gt;Another problem with running separate build agents inside each virtual machine is that we would have to keep all of the virtual machines running all of the time.&amp;#160; Otherwise, the TeamCity primary server would not be able to connect to the build agents.&amp;#160; Running so many virtual machines (dozens?) would be a prohibitively expensive waste of resources.&amp;#160; Moreover, we would need to keep a lot of build agents running and it costs money to buy licenses for more than 3.&lt;/p&gt;  &lt;p&gt;Our solution will be to run TeamCity build agents only on the virtual machine host (or in a virtual machine with access to the host).&amp;#160; Each build task will be responsible for starting up the appropriate virtual machine, invoking a remote command inside the virtual machine, gathering results from inside the virtual machine, and then shutting down the virtual machine (discarding changes to its state).&lt;/p&gt;  &lt;p&gt;All we need is one TeamCity build agent!&amp;#160; That one build agent can manage as many virtual machines as we like.&lt;/p&gt;  &lt;p&gt;In practical terms, we’ll probably run 3 TeamCity build agents.&amp;#160; That way we can run 3 tasks in parallel to improve performance while still fitting within the license constraints of the free TeamCity Professional Edition.&lt;/p&gt;  &lt;h5&gt;Odds and Ends&lt;/h5&gt;  &lt;p&gt;We’re also going to need a few other things.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Test reporting.&amp;#160; (&lt;a href="http://blog.bits-in-motion.com/2008/04/gallio-archimedes.html"&gt;Gallio Archimedes&lt;/a&gt;)&lt;/li&gt;    &lt;li&gt;Code coverage.&amp;#160; (&lt;a href="http://www.ncover.com/"&gt;NCover&lt;/a&gt; + &lt;a href="http://blog.bits-in-motion.com/2008/04/gallio-archimedes.html"&gt;Gallio Archimedes&lt;/a&gt;)&lt;/li&gt;    &lt;li&gt;Linting.&amp;#160; (&lt;a href="http://www.ndepend.com/"&gt;NDepend&lt;/a&gt; + &lt;a href="http://msdn.microsoft.com/en-us/library/bb429476%28VS.80%29.aspx"&gt;FxCop&lt;/a&gt; + &lt;a href="http://blog.bits-in-motion.com/2008/04/gallio-archimedes.html"&gt;Gallio Archimedes&lt;/a&gt;)&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Err, right… I guess we need to build Archimedes too…&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:8487f97d-2e9e-4c88-bf22-87ab3c920905" class="wlWriterEditableSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;,&lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;,&lt;a href="http://technorati.com/tags/software+engineering" rel="tag"&gt;software engineering&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/H3XFgUo2f3Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/8470227546391059045/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=8470227546391059045" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/8470227546391059045?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/8470227546391059045?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2010/02/setting-up-distributed-build-part-1.html" title="Setting Up a Distributed Build – Part 1" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;A0QNQ3cyfyp7ImA9WxBWGUw.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-5753965978082426890</id><published>2010-02-11T12:13:00.001-08:00</published><updated>2010-02-11T12:16:32.997-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-11T12:16:32.997-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Gallio Build Server Down for Maintenance</title><content type="html">&lt;p&gt;The build server has been in an unhappy state for quite some time now (registry corruption due to frequent crashes of the VirtualBox VM).&amp;#160; Now it finally seems to have gone over the edge.&lt;/p&gt;  &lt;p&gt;As a result I’ll be rebuilding the build server sometime in the next week.&amp;#160; I think I'll set the new one up a bit differently if I have time.&lt;/p&gt;  &lt;h4&gt;Long Term Ideas&lt;/h4&gt;  &lt;h5&gt;1. Split up the build based on component dependencies.&lt;/h5&gt;  &lt;p&gt;Gallio ships with plugins for ReSharper 3.1, 4.0, 4.1, 4.5 and 5.0.&amp;#160; The trick here is that in order to build and test those plugins, all 5 versions of ReSharper need to be installed.&amp;#160; Ouch!&amp;#160; (Ok, we will probably be dropping 3.1, 4.0 and 4.1 soon…)&lt;/p&gt;  &lt;p&gt;Of course Gallio also ships with plugins for NCover, TypeMock, AutoCAD, TestDriven.Net, Visual Studio 2008, Visual Studio 2010, etc…&lt;/p&gt;  &lt;p&gt;Imagine all of those components installed on the same VM?&amp;#160; Yeah… it’s a little scary.&amp;#160; Fortunately most of them do not conflict with the others so it mostly works out fine.&lt;/p&gt;  &lt;p&gt;Ideally we should just build a bunch of VMs with different groups of dependencies and build them all independently.&amp;#160; That way we can add new dependencies at will without worrying quite so much about how they interact (or worry about the 12 hours it will take to reinstall everything if the VM blows up).&lt;/p&gt;  &lt;h5&gt;2. Multiple Platforms&lt;/h5&gt;  &lt;p&gt;We should really be testing on at least the following platforms:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Windows 7 64bit &lt;/li&gt;    &lt;li&gt;Windows 7 32bit &lt;/li&gt;    &lt;li&gt;Mono on Windows &lt;/li&gt;    &lt;li&gt;Mono on Linux&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Basically that means we’ll need a whole bunch of VMs for various configurations.&amp;#160; Tricky.&lt;/p&gt;  &lt;h5&gt;3. Add White Box Tests.&lt;/h5&gt;  &lt;p&gt;Right now there are a bunch of things we don’t test rigorously in an automated way.&amp;#160; Here are a few of them:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;The installer. &lt;/li&gt;    &lt;li&gt;The Visual Studio templates. &lt;/li&gt;    &lt;li&gt;The Visual Studio add-in, besides checking MSTest integration. &lt;/li&gt;    &lt;li&gt;The ReSharper plugin, besides integration tests for the reflection API. &lt;/li&gt;    &lt;li&gt;The UIs, besides unit tests for the Models and Presenters. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;I spend a lot of time before each release manually testing this stuff.&amp;#160; It just doesn’t scale.&lt;/p&gt;  &lt;p&gt;All we really need are a few tests to get sufficient confidence that the system isn’t broken in really boneheaded ways.&amp;#160; For example, Visual Studio shouldn’t pop up a dialog complaining about the Gallio add-in encountering an error during initialization.&amp;#160; (Usually that means the installer is broken somehow.)&lt;/p&gt;  &lt;h5&gt;4. Faster Release Cycles&lt;/h5&gt;  &lt;p&gt;All it should take for a release is for us to spend a couple of minutes writing release notes, adding some more documentation to the wiki, and pushing a &lt;font color="#ff0000"&gt;big red button&lt;/font&gt; to promote the current nightly build to &lt;em&gt;stable&lt;/em&gt;.&lt;/p&gt;  &lt;p&gt;Paradoxically, the first step in this process is that &lt;em&gt;unstable&lt;/em&gt; builds need to be more visible.&lt;/p&gt;  &lt;p&gt;What I mean is that we should maintain consistently up to date release notes and documentation for our pre-release builds too.&amp;#160; We should get more people using those pre-release builds and perform more sanity-checking with automation so we’re not chasing last-minute bugs.&lt;/p&gt;  &lt;p&gt;When it comes time to release, all we should have to do is post a new stable build label to the website and spam the newsgroups.&amp;#160; :-)&lt;/p&gt;  &lt;p&gt;Duh.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:df0f76be-a81c-4934-82b5-b2fa87bc989d" class="wlWriterEditableSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;,&lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/M6c8YhtizL8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/5753965978082426890/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=5753965978082426890" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5753965978082426890?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5753965978082426890?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2010/02/gallio-build-server-down-for.html" title="Gallio Build Server Down for Maintenance" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;CE8NRH0_cCp7ImA9WxBVGUo.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-4723492284923566955</id><published>2010-02-06T21:19:00.001-08:00</published><updated>2010-02-23T16:54:55.348-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-23T16:54:55.348-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Infrastructure and Documentation</title><content type="html">&lt;p&gt;With my new job at Google, I’ve found it harder and harder to spend as much time as I would like on Gallio and MbUnit.&amp;#160; It’s hard to find a good solid block of time to work on stuff without too many other distractions so I’ve been spending my time on little infrastructure projects.&lt;/p&gt;  &lt;p&gt;The theory is that if I can build a big enough lever… then I can pretend there really are 32 hours in a day.&amp;#160; :-)&lt;/p&gt;  &lt;h4&gt;New Stuff&lt;/h4&gt;  &lt;h5&gt;1. Auto-Publish to Web&lt;/h5&gt;  &lt;p&gt;Most of the Gallio and MbUnit web site content including documentation resources are now published to the web automatically by our &lt;a href="http://ccnet.gallio.org/"&gt;Continuous Integration build server&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;In other words, any committer can change the website just by checking in some code, and waiting 3-5 minutes for the web site to be built and the changes to propagate.&lt;/p&gt;  &lt;p&gt;This kind of setup is really cool.&amp;#160; Seriously seriously cool.&amp;#160; So cool I know &lt;a href="http://ayende.com/"&gt;Oren&lt;/a&gt; has &lt;a href="http://ayende.com/Blog/archive/2009/08/21/nh-prof-error-handling-continuous-integration-and-rtm-per-commit.aspx"&gt;blogged about it&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Given my time resources are dwindling (and will even more once I start working on Android stuff), automating the whole pipeline is golden.&amp;#160; I can practically pretend that it’s real-time.&amp;#160; Hit commit and move on to another task.&lt;/p&gt;  &lt;h4&gt;2. New Wiki!&lt;/h4&gt;  &lt;p&gt;Let me be blunt.&amp;#160; The Gallio documentation is woefully incomplete.&lt;/p&gt;  &lt;p&gt;The main problem is that I expected that the &lt;a href="http://www.gallio.org/book/XHtml/index.html"&gt;Gallio Book&lt;/a&gt; writing to progress more rapidly than it has so I didn’t spend much time developing alternative documentation resources.&lt;/p&gt;  &lt;p&gt;Of course, there have been many offers to help write content for the book but little has materialized so far.&amp;#160; That’s just how Open Source is sometimes.&amp;#160; Lots of people want to help but there is always a barrier to contribution.&amp;#160; In this case I’ve lowered the barrier a bit by automating the book pipeline from commit through to publication on the web but writing high quality documentation still takes time and preparation (especially in book form).&lt;/p&gt;  &lt;p&gt;So…&lt;/p&gt;  &lt;p&gt;Say hello to quick &amp;amp; dirty documentation by the masses for the masses.&lt;/p&gt;  &lt;p&gt;The new &lt;a href="http://www.gallio.org/wiki"&gt;Gallio Wiki&lt;/a&gt; is here!&lt;/p&gt;  &lt;p&gt;It’s a little empty...&amp;#160; Please help fill it up.&amp;#160; :-)&lt;/p&gt;  &lt;p&gt;Also, if you are interested, the old MbUnit v2 Wiki is still available &lt;strike&gt;on the &lt;/strike&gt;&lt;a href="http://web.archive.org/web/20070816103008/www.mertner.com/confluence/display/MbUnit/Site+Index"&gt;&lt;strike&gt;Internet Archive&lt;/strike&gt;&lt;/a&gt; on the &lt;a href="http://www.gallio.org/oldwiki/MbUnit/index.html"&gt;Gallio website&lt;/a&gt;.&amp;#160; We should probably try to port some of this content over, or something.&lt;/p&gt;  &lt;h4&gt;3. Old MbUnit v2 Documentation Is Back&lt;/h4&gt;  &lt;p&gt;A couple of years ago, &lt;a href="http://blog.benhall.me.uk/"&gt;Ben Hall&lt;/a&gt; invested quite a lot of time &lt;a href="http://blog.benhall.me.uk/2007/07/sandcastle-and-docproject.html"&gt;getting Sandcastle and DocProject to play nicely together&lt;/a&gt; and assembling the documentation.&lt;/p&gt;  &lt;p&gt;Unfortunately when we moved the &lt;a href="http://www.mbunit.com/"&gt;MbUnit.com&lt;/a&gt; website a couple of years ago, we lost some content.&amp;#160; Specifically, we lost the documentation site.&lt;/p&gt;  &lt;p&gt;Oh… we still had the code to generate the documentation site, of course, but the specific versions of &lt;a href="http://www.codeplex.com/Sandcastle"&gt;Sandcastle&lt;/a&gt; and &lt;a href="http://www.codeplex.com/DocProject"&gt;DocProject&lt;/a&gt; that we used were no longer available and the new versions were not compatible with the old code.&lt;/p&gt;  &lt;p&gt;Great.&amp;#160; Bit rot.&amp;#160; For two years, I was sitting on a pile of documentation that I couldn’t compile.&lt;/p&gt;  &lt;p&gt;Last night I decided to bite the bullet and try again.&amp;#160; It was a total nightmare!&amp;#160; The whole ordeal took me maybe 12 hours.&amp;#160;&amp;#160; I spent a good 8 hours trying to upgrade to a newer version of DocProject and getting nowhere fast.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Edit: I should point out that I don't blame my problems on DocProject or Sandcastle themselves. The real problem is that I didn't really understand how it all worked so I wasted a lot of time trying stupid things that failed.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Today I finally gave up and ported the essential parts of the documentation to the latest version of &lt;a href="http://www.codeplex.com/Sandcastle"&gt;Sandcastle&lt;/a&gt; and the &lt;a href="http://www.codeplex.com/SHFB"&gt;Sandcastle Help File Builder&lt;/a&gt;.&amp;#160; It’s a little worse for wear but it’s still readable.&lt;/p&gt;  &lt;p&gt;Anyways, now the old MbUnit v2 documentation is online again here: &lt;a href="http://www.gallio.org/api-v2/index.aspx"&gt;MbUnit v2 Documentation&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Maybe we can port some of that content to the &lt;a href="http://www.gallio.org/wiki"&gt;wiki&lt;/a&gt; or &lt;a href="http://www.gallio.org/book/XHtml/index.html"&gt;book&lt;/a&gt; and update it for MbUnit v3…&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:628de361-3464-4af9-b41d-55b5df42c0e7" class="wlWriterSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;,&lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;&lt;/div&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Edit: Posted new link to old MbUnit v2 wiki content.&lt;/em&gt;&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/2NOkdVdcXi8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/4723492284923566955/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=4723492284923566955" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/4723492284923566955?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/4723492284923566955?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2010/02/infrastructure-and-documentation.html" title="Infrastructure and Documentation" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;CU8BQn0zeyp7ImA9WxBXEE4.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-8396207615252410118</id><published>2009-11-18T09:00:00.001-08:00</published><updated>2010-01-20T16:30:53.383-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-20T16:30:53.383-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="release notes" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Announcing Gallio v3.1 Update 2</title><content type="html">&lt;p&gt;Today we are releasing Gallio v3.1 Update 2.&amp;#160; This releases fixes several problems on x64 platforms and includes support for Visual Studio 2010 and .Net Framework 4.0 Beta 2.&lt;/p&gt;  &lt;p&gt;Download here: &lt;a href="http://www.gallio.org/Downloads.aspx"&gt;http://www.gallio.org/Downloads.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Documentation here: &lt;a href="http://www.gallio.org/Docs.aspx"&gt;http://www.gallio.org/Docs.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Earlier release notes: &lt;a href="http://blog.bits-in-motion.com/2009/10/announcing-gallio-and-mbunit-v31-update.html"&gt;v3.1 update 1&lt;/a&gt;, &lt;a href="http://blog.bits-in-motion.com/2009/09/announcing-gallio-and-mbunit-v31.html"&gt;v3.1&lt;/a&gt;, &lt;a href="http://blog.bits-in-motion.com/search/label/release%20notes"&gt;all versions&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;  &lt;h4&gt;Changes&lt;/h4&gt;  &lt;ul&gt;   &lt;li&gt;Improved startup performance by fixing a problem with pre-generated &lt;a href="http://blog.bits-in-motion.com/2009/11/xmlserializers-moduleversionid-ilmerge.html"&gt;XmlSerializers&lt;/a&gt;.&lt;/li&gt;    &lt;li&gt;Added support for Visual Studio 2010 and .Net Framework 4.0 Beta 2.&lt;/li&gt;    &lt;li&gt;Fixed an AccessViolationException in Icarus.&amp;#160; Special thanks to Kent Hansen for identifying the root cause and proposing a fix!&lt;/li&gt;    &lt;li&gt;Fixed installer bugs on x64 which caused some components to not be installed.&lt;/li&gt;    &lt;li&gt;Fixed bugs running MSTest tests on x64.&lt;/li&gt;    &lt;li&gt;Fixed a problem that caused long-running tests to be aborted prematurely by Visual Studio.&lt;/li&gt; &lt;/ul&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:cabebf40-972b-4328-990a-cb1e51727fdd" class="wlWriterEditableSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;,&lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/wUTe8dr4Gf8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/8396207615252410118/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=8396207615252410118" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/8396207615252410118?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/8396207615252410118?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/11/announcing-gallio-v31-update-2.html" title="Announcing Gallio v3.1 Update 2" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;DU8BQXw_fSp7ImA9WxNbEEw.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-917529186764974009</id><published>2009-11-12T01:24:00.001-08:00</published><updated>2009-11-12T01:24:10.245-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-12T01:24:10.245-08:00</app:edited><title>XmlSerializers, ModuleVersionId, ILMerge, and You</title><content type="html">&lt;p&gt;I solved a minor mystery today.&lt;/p&gt;  &lt;p&gt;Gallio internally uses the .Net &lt;a href="http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx"&gt;XmlSerializer&lt;/a&gt; class to load plug-in metadata and save reports.&amp;#160; You probably know all about XmlSerializer already, but maybe you don’t know just how badly using it can impact application start-up performance.&lt;/p&gt;  &lt;h4&gt;About XmlSerializer Code Generation&lt;/h4&gt;  &lt;p&gt;The heart of the issue is that XmlSerializer uses code generation to perform serialization and deserialization.&amp;#160; Specifically, XmlSerializer generates some fresh C# code, compiles it out of process with &lt;strong&gt;csc.exe&lt;/strong&gt;, then loads the resulting assembly.&amp;#160; This work takes time.&amp;#160; Seconds… feels like forever to a user.&amp;#160; The generated assemblies are not cached across runs so there is a significant performance penalty on every launch.&lt;/p&gt;  &lt;p&gt;There is a way to avoid this cost: pre-generate serializers assemblies and distribute them with the application.&lt;/p&gt;  &lt;p&gt;To pre-generate a serializers assembly, you are supposed to use &lt;a href="http://msdn.microsoft.com/en-us/library/bk3w6240%28VS.80%29.aspx"&gt;SGen.exe&lt;/a&gt; a bit like this:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;SGen.exe /assembly:MyAssembly /type:MyRootXmlType&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This command will emit MyAssembly.XmlSerializers.dll.&amp;#160; All you need to do then is copy it next to MyAssembly.dll and you’re done.&amp;#160; The cost of code generation is gone from every launch.&lt;/p&gt;  &lt;p&gt;Two problems:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;SGen only lets you specify one root type.&amp;#160; If you use multiple Xml document types in your assembly then you will need to write a custom tool like this: &lt;a href="http://code.google.com/p/mb-unit/source/browse/#svn/trunk/v3/tools/Gallio.BuildTools/Gallio.BuildTools.SGen"&gt;Gallio.BuildTools.SGen&lt;/a&gt;.&lt;/li&gt;    &lt;li&gt;Make absolutely sure that you keep MyAssembly.dll and MyAssembly.XmlSerializers.dll in sync.&amp;#160; If you change MyAssembly.dll&lt;em&gt; in any way&lt;/em&gt; then you must regenerate the serializers assembly.&amp;#160; This is easy to set up once in your build scripts and forget about.&lt;/li&gt; &lt;/ol&gt;  &lt;h4&gt;Troubleshooting&lt;/h4&gt;  &lt;p&gt;So let’s say you do all of this work and you run your program and you still see &lt;strong&gt;csc.exe&lt;/strong&gt; starting up while your program runs.&lt;/p&gt;  &lt;p&gt;To find out why, add the following to your application’s config file.&amp;#160; (eg. MyApp.exe.config)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;lt;configuration&amp;gt;&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;system.diagnostics&amp;gt;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;switches&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;add name=”XmlSerialization.PregenEventLog” value=”1” /&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/switches&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/system.diagnostics&amp;gt;&lt;/p&gt;    &lt;p&gt;&amp;lt;/configuration&amp;gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Then run your program again and look at the Windows Event Log.&amp;#160; There should be some information in there to help you out.&amp;#160; (Another thing to try is &lt;a href="http://msdn.microsoft.com/en-us/library/e74a18c4%28VS.71%29.aspx"&gt;fuslogvw.exe&lt;/a&gt;.)&lt;/p&gt;  &lt;p&gt;Here’s the message that I saw:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Pre-generated serializer ‘Gallio.XmlSerializers’ has expired. You need to re-generate serializer for ‘Gallio.Runtime.Extensibility.Schema.Cache’.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This message is telling me that I violated rule #2 above: the serializers assembly must &lt;em&gt;always&lt;/em&gt; be kept in sync with its parent assembly!&lt;/p&gt;  &lt;p&gt;Why?&amp;#160; I’ve got fancy build scripts…&lt;/p&gt;  &lt;h4&gt;ModuleVersionId&lt;/h4&gt;  &lt;p&gt;XmlSerializer verifies that the serializer assembly is in sync with its parent assembly by comparing the module version id of the parent assembly with an id that it previously cached when it generated the serializer assembly.&lt;/p&gt;  &lt;p&gt;Let’s check whether the assemblies are in sync using &lt;a href="http://www.red-gate.com/products/reflector/"&gt;Reflector&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Gallio.dll is the parent assembly, it has a module version id of “fbe34432-817a-46dd-832f-3e5bc679ecff”.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_X_hvY6E0egY/SvvUNDWS-nI/AAAAAAAAAcQ/XTtRa7C60so/s1600-h/image%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_X_hvY6E0egY/SvvUNkB1gPI/AAAAAAAAAcU/DlavdhJg-nE/image_thumb%5B1%5D.png?imgmax=800" width="290" height="91" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Gallio.XmlSerializers.dll is the serializers assembly, it has a special assembly-level attribute called XmlSerializerVersion that was emitted during code generation.&amp;#160; It expects the parent assembly id to be “3c420916-3f3c-45cb-a79c-9a4bbc81014a&amp;quot;.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Pedantic note: An assembly can have multiple modules each and each module has its own id.&amp;#160; So the XmlSerializerVersion attribute actually contains a comma-delimited list of all module version ids of the parent asssembly sorted in increasing order.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SvvUN8lhCzI/AAAAAAAAAcY/195HZU3UBKM/s1600-h/image%5B7%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SvvUOSKllZI/AAAAAAAAAcc/YDeD1sCpyjU/image_thumb%5B3%5D.png?imgmax=800" width="612" height="127" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Ok, so XmlSerializer thinks the pre-generated serializers assembly is out of sync because the module id is out of sync.&amp;#160; That seems bananas because my build scripts always regenerate Gallio.XmlSerializers.dll whenever Gallio.dll is recompiled.&lt;/p&gt;  &lt;h4&gt;ILMerge&lt;/h4&gt;  &lt;p&gt;Well, my build scripts do one more thing: they run &lt;a href="http://research.microsoft.com/en-us/people/mbarnett/ILMerge.aspx"&gt;ILMerge&lt;/a&gt; on Gallio.dll to internalize certain dependencies that might otherwise get in the way of running unit tests.&amp;#160; (Bad things would happen if your unit testing tool exposed a dependency on one version of Mono.Cecil.dll whereas your tests depended on a different version.)&lt;/p&gt;  &lt;p&gt;When ILMerge internalizes dependencies, it has to regenerate the assembly.&amp;#160; As a result, the new freshly merged Gallio.dll gets a brand new ModuleVersionId.&amp;#160; Unfortunately we pre-generated Gallio.XmlSerializers.dll before running ILMerge so it is now out of sync.&lt;/p&gt;  &lt;p&gt;All we need to do is regenerate Gallio.XmlSerializers.dll after the ILMerge.&amp;#160; Problem solved.&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/F-mTI_LEdJk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/917529186764974009/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=917529186764974009" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/917529186764974009?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/917529186764974009?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/11/xmlserializers-moduleversionid-ilmerge.html" title="XmlSerializers, ModuleVersionId, ILMerge, and You" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_X_hvY6E0egY/SvvUNkB1gPI/AAAAAAAAAcU/DlavdhJg-nE/s72-c/image_thumb%5B1%5D.png?imgmax=800" height="72" width="72" /><thr:total>5</thr:total></entry><entry gd:etag="W/&quot;CU8CRXo6fip7ImA9WxBXEE4.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-848552124988355738</id><published>2009-10-26T00:55:00.001-07:00</published><updated>2010-01-20T16:31:04.416-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-20T16:31:04.416-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="release notes" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Announcing Gallio and MbUnit v3.1 Update 1</title><content type="html">&lt;p&gt;Today we are releasing Gallio and MbUnit v3.1 Update 1.&amp;#160; This is mainly a bug fix release with a few new little goodies.&lt;/p&gt;  &lt;p&gt;Download here: &lt;a href="http://www.gallio.org/Downloads.aspx"&gt;http://www.gallio.org/Downloads.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Documentation here: &lt;a href="http://www.gallio.org/Docs.aspx"&gt;http://www.gallio.org/Docs.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Earlier release notes: &lt;a href="http://blog.bits-in-motion.com/2009/09/announcing-gallio-and-mbunit-v31.html"&gt;v3.1&lt;/a&gt;, &lt;a href="http://blog.bits-in-motion.com/search/label/release%20notes"&gt;all versions&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Reminder: The Visual Studio Test Tools integration requires Visual Studio 2008 SP1 or Visual Studio 2010 to work.&amp;#160; If you still don't see tests in the Test View then ensure that your project has the necessary &lt;a href="http://blog.bits-in-motion.com/2009/04/how-to-enable-or-disable-visual-studio.html"&gt;ProjectTypeGuids&lt;/a&gt; and that the Gallio add-in is enabled in the Visual Studio Add-In Manager.&lt;/em&gt;&lt;/p&gt;  &lt;h3&gt;Changes&lt;/h3&gt;  &lt;h5&gt;Samples:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;[New!] &lt;/strong&gt;Added samples for web testing with &lt;a href="http://watin.sourceforge.net/"&gt;WatiN&lt;/a&gt;.&amp;#160; In particular demonstrates the creation of a custom [Browser] attribute to support running tests with multiple browsers.&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;[New!]&lt;/strong&gt; Added samples for GUI testing with &lt;a href="http://www.codeplex.com/white"&gt;White&lt;/a&gt;.&lt;/li&gt;    &lt;li&gt;Improved basic MbUnit samples a little to make them easier to understand.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;General:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;[New!]&lt;/strong&gt; Added a &lt;em&gt;Sources.txt&lt;/em&gt; file to the installation to help users find out where to check out the original source code for the release.&lt;/li&gt;    &lt;li&gt;Fixed a bug with Hint Directories being ignored by the test runners.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;MbUnit:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Upgraded MVC templates for ASP.Net MVC 2.&lt;/li&gt;    &lt;li&gt;Fixed bug with transitive dependencies not being considered by [DependsOn] attribute when deciding whether to skip a test.&lt;/li&gt;    &lt;li&gt;Improved the error message displayed when attempting to use a [StaticTestFactory] in ReSharper which is not supported due to technical limitations.&lt;/li&gt;    &lt;li&gt;Fixed bug with use of [Factory] attribute on a fixture type or property when the type was not explicitly specified.&lt;/li&gt;    &lt;li&gt;Improved error reporting and workarounds for cases where the assertions or test context are being used incorrectly to help a test author diagnose the problem.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Icarus: &lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;[New!]&lt;/strong&gt; Added support for using the Delete key to delete items from the Project Explorer.&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;[New!] &lt;/strong&gt;Pending and Ignored tests are now unchecked by default.&lt;/li&gt;    &lt;li&gt;Added filtering to the test report view to only include reports that match the current report name format.&lt;/li&gt;    &lt;li&gt;Fixed some threading issues.&lt;/li&gt;    &lt;li&gt;Fixed some test filter issues.&lt;/li&gt;    &lt;li&gt;Fixed test counts.&lt;/li&gt;    &lt;li&gt;Fixed window positioning issues.&lt;/li&gt;    &lt;li&gt;Fixed display of test categories.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Reports:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;[New!] &lt;/strong&gt;Added support for viewing embedded text, html and video attachments in Icarus and Visual Studio.&lt;/li&gt;    &lt;li&gt;Fixed rendering of embedded plain text attachments to preserve formatting.&lt;/li&gt;    &lt;li&gt;Fixed UTF-8 character encoding issue with html reports.&lt;/li&gt;    &lt;li&gt;Fixed attachment links and code navigation links in reports on 64-bit platforms and in Internet Explorer Protected Mode.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;xUnit.Net Adapter:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Upgraded to v1.5 RTM.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;AutoCAD Integration:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Improved detection of AutoCAD window.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;ReSharper Integration:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Added support for certain reflection operations which were needed by NUnit to support derived test fixtures in ReSharper.&lt;/li&gt;    &lt;li&gt;Fixed a bug that caused duplicate NUnit tests to appear in ReSharper.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;NAnt Integration:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Changed how the severity of certain test results are reported so that NAnt does not incorrectly consider a test run to have encountered a non-fatal error when in fact a warning should have been logged.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;TeamCity Integration:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Fixed a bug in the test event processing that indirectly caused TeamCity test result reporting to stall when an empty test assembly was encountered.&lt;/li&gt; &lt;/ul&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:a01782fa-5773-433e-a526-b515aacb8056" class="wlWriterSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;,&lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/nhrgJuPybMM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/848552124988355738/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=848552124988355738" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/848552124988355738?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/848552124988355738?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/10/announcing-gallio-and-mbunit-v31-update.html" title="Announcing Gallio and MbUnit v3.1 Update 1" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;D0YBRnY9cCp7ImA9WxNQEkg.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-5881604102547479890</id><published>2009-09-17T23:20:00.001-07:00</published><updated>2009-09-17T23:25:57.868-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T23:25:57.868-07:00</app:edited><title>Gallio Next Steps</title><content type="html">&lt;p&gt;With Gallio v3.1 released, I have begun planning for what's ahead.&lt;/p&gt;  &lt;p&gt;My first priority is to fix most of the issues that have been reported and to release a minor update to Gallio v3.1 in a week or two.&lt;/p&gt;  &lt;p&gt;After that, the hard work begins.&lt;/p&gt;  &lt;h4&gt;Testing as a Service&lt;/h4&gt;  &lt;p&gt;As it stands, the Gallio platform mainly consists of one core library and a whole bunch of plug-ins.&amp;#160; The majority of the code is written in C# and runs on .Net Framework 2.0.&lt;/p&gt;  &lt;p&gt;I want to go further.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;The main goal of Gallio v3.2&lt;/em&gt; will be to adapt the Gallio platform to operate as a collection of services that communicate via a message bus.&amp;#160; We will publish a specification of the messaging protocol so that other tools -- not necessarily written in .Net -- will be able to exchange messages with Gallio tools and participate in test execution, monitoring and reporting.&lt;/p&gt;  &lt;p&gt;Why not pool our talents to develop great testing tools that work together across platform boundaries?&lt;/p&gt;  &lt;h4&gt;Gallio v3.2 and Beyond&lt;/h4&gt;  &lt;p&gt;Here's what you can expect from upcoming releases.&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Open messaging protocol.&lt;/li&gt;    &lt;li&gt;Test data warehouse.&lt;/li&gt;    &lt;li&gt;Web-based test reporting tool and test case manager.&amp;#160; (Archimedes)&lt;/li&gt;    &lt;li&gt;Test rigs for remote test execution.&lt;/li&gt;    &lt;li&gt;Data collection API for monitoring performance, gathering metrics and computing statistics.&lt;/li&gt;    &lt;li&gt;Improved IDE integration.&lt;/li&gt;    &lt;li&gt;Improved cross-platform support.&lt;/li&gt;    &lt;li&gt;More openness about the development process in general.&lt;/li&gt;    &lt;li&gt;Performance optimization, particularly of UI responsiveness and program start time.&lt;/li&gt; &lt;/ol&gt;  &lt;h4&gt;How to Participate&lt;/h4&gt;  &lt;p&gt;I want to work with other members of the community to grow a practical &lt;em&gt;lingua franca&lt;/em&gt; for test data interchange.&amp;#160; While at first we will be discussing how to design a suitable messaging protocol for Gallio, I hope that it will eventually evolve into an independent open standard that anyone can implement.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Please join us on the &lt;a href="http://groups.google.com/group/gallio-dev"&gt;gallio-dev&lt;/a&gt; mailing list and tell us what you think!&amp;#160; :-)&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/tlQKZa6zr9w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/5881604102547479890/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=5881604102547479890" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5881604102547479890?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5881604102547479890?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/09/gallio-next-steps.html" title="Gallio Next Steps" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;D0AEQHg8fSp7ImA9WxNRGUQ.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-1932538451854548391</id><published>2009-09-14T23:21:00.001-07:00</published><updated>2009-09-14T23:21:41.675-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-14T23:21:41.675-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="ioc" /><category scheme="http://www.blogger.com/atom/ns#" term="architecture" /><title>The Gallio Plug-in Model</title><content type="html">&lt;p&gt;One of the biggest changes in Gallio v3.1 was is a new plug-in model.&lt;/p&gt;  &lt;p&gt;The new plug-in model features comprehensive support for late-binding of components, provides a rich declarative metadata model, and provides enough information to support the installation and verification of plug-in assemblies and resources.&lt;/p&gt;  &lt;h4&gt;Concepts&lt;/h4&gt;  &lt;h5&gt;Late-Binding and Declarative Metadata&lt;/h5&gt;  &lt;p&gt;Gallio includes many plug-ins out of the box and more can be installed later but not all of the plug-ins need to be loaded at all times.&amp;#160; To improve startup time, Gallio defers plug-in assembly loading until the last possible moment.&amp;#160; Consequently Gallio's plug-in model relies heavily on late-binding and declarative metadata that is external to the plug-in assemblies.&lt;/p&gt;  &lt;p&gt;We &lt;em&gt;don't&lt;/em&gt; use .Net custom attributes to provide most of this metadata because then we would have to load assemblies in order to read it out.&amp;#160; (Note: We could read metadata from custom attributes once and cache it like MEF does but it is not practical for some purposes such as plug-in installation.)&lt;/p&gt;  &lt;p&gt;So the basic idea is to pack a bunch of useful information in XML.&lt;/p&gt;  &lt;h5&gt;Drop-In Plug-In Installation and Caching&lt;/h5&gt;  &lt;p&gt;Gallio strives to support XCopy-style deployment as much as possible.&amp;#160; This feature makes it possible to check-out a copy of Gallio from your source tree and just run it with no strings attached (except for special features that require installation like Visual Studio integration).&lt;/p&gt;  &lt;p&gt;When designing the Gallio plug-in model it was important to ensure that Gallio plug-ins could be installed just by dropping files in the right place.&amp;#160; At run-time Gallio locates its plug-ins by searching plug-in directories recursively for .plugin files.&amp;#160; There is one major problem with this process: recursively scanning directories and loading lots of little files can be very time-consuming.&amp;#160; So the new plug-in model incorporates a cache.&lt;/p&gt;  &lt;p&gt;Gallio maintains a per-user cache of installed plug-ins in any given list of plug-in directories.&amp;#160; This way when it starts up, it only needs to load the cache file and move on.&lt;/p&gt;  &lt;p&gt;However there is a small problem with this approach.&amp;#160; By caching plug-in metadata, it is no longer sufficient to just drop in new plug-in files to install them.&amp;#160; The cache must be cleared somehow.&lt;/p&gt;  &lt;p&gt;Gallio implements three strategies for refreshing its cache:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;When loading a cache file, it checks the timestamps on the original plug-in files to verify that they have not been changed.&amp;#160; This way Gallio can automatically detect an edit to a .plugin file and refresh the cache.&amp;#160; Unfortunately it cannot detect the presence of new .plugin files.&lt;/li&gt;    &lt;li&gt;A user can explicitly clear the plug-in cache using the&amp;#160; &lt;em&gt;&amp;quot;Gallio.Utility.exe ClearUserPluginCache&amp;quot; &lt;/em&gt;command.&amp;#160; Unfortunately if there are multiple users of the machine then all users will need to have their plug-in cache cleared to detect the presence of a new plug-in.&lt;/li&gt;    &lt;li&gt;A admin user can explicitly reset the Gallio &amp;quot;installation id&amp;quot; of the machine by running &lt;em&gt;&amp;quot;Gallio.Utility.exe ResetInstallationId&amp;quot;&lt;/em&gt;.&amp;#160; This command effectively invalidates the caches of all users running on the machine at once.&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;It's not ideal but it works well for now.&amp;#160; Plug-in metadata caching has significantly improved Gallio start-up performance.&lt;/p&gt;  &lt;h4&gt;Plug-in Metadata&lt;/h4&gt;  &lt;h5&gt;.plugin files&lt;/h5&gt;  &lt;p&gt;Each Gallio plug-ins has a &amp;quot;.plugin&amp;quot; file that describes the plug-in using XML.&amp;#160; The description includes the id of the plug-in, some traits, dependencies, associated files, referenced assemblies, services and components.&lt;/p&gt;  &lt;p&gt;Here is an excerpt of &lt;em&gt;Gallio.plugin&lt;/em&gt;.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;     &lt;br /&gt;&amp;lt;plugin pluginId=&amp;quot;Gallio&amp;quot; recommendedInstallationPath=&amp;quot;&amp;quot; xmlns=&amp;quot;&lt;a href="http://www.gallio.org/"&gt;http://www.gallio.org/&amp;quot;&lt;/a&gt;&amp;gt;      &lt;br /&gt;&amp;#160; &amp;lt;traits&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;name&amp;gt;Gallio&amp;lt;/name&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;version&amp;gt;0.0.0.0&amp;lt;/version&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;description&amp;gt;The heart of Gallio.&amp;lt;/description&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;icon&amp;gt;plugin://Gallio/Resources/Gallio.ico&amp;lt;/icon&amp;gt;      &lt;br /&gt;&amp;#160; &amp;lt;/traits&amp;gt; &lt;/p&gt;    &lt;p&gt;&amp;#160; &amp;lt;dependencies&amp;gt;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;dependency pluginId=&amp;quot;BuiltIn&amp;quot; /&amp;gt;      &lt;br /&gt;&amp;#160; &amp;lt;/dependencies&amp;gt; &lt;/p&gt;    &lt;p&gt;&amp;#160; &amp;lt;files&amp;gt;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;file path=&amp;quot;Gallio.plugin&amp;quot; /&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;file path=&amp;quot;Gallio.dll&amp;quot; /&amp;gt;&lt;/p&gt;    &lt;p&gt;&amp;#160;&lt;strong&gt;&amp;#160;&amp;#160; &amp;lt;!-- SNIP --&amp;gt;&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&amp;#160; &amp;lt;/files&amp;gt; &lt;/p&gt;    &lt;p&gt;&amp;#160; &amp;lt;assemblies&amp;gt;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;assembly fullName=&amp;quot;Gallio, Version=0.0.0.0, Culture=neutral, PublicKeyToken=eb9cfa67ee6ab36e&amp;quot; codeBase=&amp;quot;Gallio.dll&amp;quot; qualifyPartialName=&amp;quot;true&amp;quot; /&amp;gt;      &lt;br /&gt;&amp;#160; &amp;lt;/assemblies&amp;gt;&lt;/p&gt;    &lt;p&gt;&amp;#160; &amp;lt;services&amp;gt;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;service serviceId=&amp;quot;Gallio.TestFramework&amp;quot;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; serviceType=&amp;quot;Gallio.Model.ITestFramework, Gallio&amp;quot; /&amp;gt; &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;service serviceId=&amp;quot;Gallio.TestFrameworkManager&amp;quot;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; serviceType=&amp;quot;Gallio.Model.ITestFrameworkManager, Gallio&amp;quot; /&amp;gt;&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;!-- SNIP --&amp;gt;&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&amp;#160; &amp;lt;/services&amp;gt;&lt;/p&gt;    &lt;p&gt;&amp;#160; &amp;lt;components&amp;gt;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;component componentId=&amp;quot;Gallio.FallbackTestFramework&amp;quot;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; serviceId=&amp;quot;Gallio.TestFramework&amp;quot;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; componentType=&amp;quot;Gallio.Model.FallbackTestFramework, Gallio&amp;quot;&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;traits&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;name&amp;gt;Fallback&amp;lt;/name&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;icon&amp;gt;plugin://Gallio/Resources/Gallio.ico&amp;lt;/icon&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/traits&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/component&amp;gt; &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;component componentId=&amp;quot;Gallio.TestFrameworkManager&amp;quot;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; serviceId=&amp;quot;Gallio.TestFrameworkManager&amp;quot;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; componentType=&amp;quot;Gallio.Model.DefaultTestFrameworkManager, Gallio&amp;quot;&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;parameters&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;fallbackTestFrameworkHandle&amp;gt;${Gallio.FallbackTestFramework}&amp;lt;/fallbackTestFrameworkHandle&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/parameters&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/component&amp;gt; &lt;/p&gt;    &lt;p&gt;&lt;strong&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;!-- SNIP --&amp;gt;&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&amp;#160; &amp;lt;/components&amp;gt;     &lt;br /&gt;&amp;lt;/plugin&amp;gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;h5&gt;Services and Components&lt;/h5&gt;  &lt;p&gt;Following the nomenclature used by several .Net Inversion of Control Containers, Gallio refers to its extension points as services and its extensions as components.&amp;#160; Services nominally represent interfaces.&amp;#160; Components provide implementations of those interfaces.&lt;/p&gt;  &lt;p&gt;Internally Gallio refers to all service and component types by name using a &lt;a href="http://www.gallio.org/api/html/T_Gallio_Common_Reflection_TypeName.htm"&gt;TypeName&lt;/a&gt; object until it needs to instantiate it.&amp;#160; This is different from most .Net IoCs which immediately resolve most type names to runtime Type objects during initialization which causes most assemblies to be loaded up front.&lt;/p&gt;  &lt;h5&gt;Dependency Injection and Configuration&lt;/h5&gt;  &lt;p&gt;Gallio's plug-in model supports constructor and property dependency injection and configuration for components.&lt;/p&gt;  &lt;p&gt;In the .plugin XML, the component parameters appear in a &lt;em&gt;&amp;lt;parameters&amp;gt;&lt;/em&gt; element which contains an element for each constructor parameter or property that is to be bound.&amp;#160; If no value is specified for a given constructor parameter, then Gallio will attempt to resolve a service of the appropriate type to bind.&amp;#160; Otherwise if a value is specified, then Gallio will attempt to convert the provided string to an object of the required type using a &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_DefaultObjectDependencyResolver.htm"&gt;DefaultObjectDependencyResolver&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;The default object dependency resolver knows how to resolve all sorts of object types including strings, arrays, components, ComponentHandles, IComponentDescriptors, Versions, Guids, Icons, Images, Conditions, FileInfos, DirectoryInfos, Assemblies, AssemblySignatures, AssemblyNames, Types, TypeNames, and explicit references to other components (using the ${component.id} syntax seen in the excerpt above.)&lt;/p&gt;  &lt;p&gt;Gallio resolves Icons, FileInfos, and other disk-based plug-in resources using a special Uri scheme called &amp;quot;plugin&amp;quot;.&amp;#160;&amp;#160; For example, a component might require a reference to a file that belongs to the plug-in so it declares a constructor parameter of type FileInfo.&amp;#160; In the XML, it provides the path of the file relative to the plug-in base directory by specifing a parameter with a Uri like: &amp;quot;plugin://MyPlugin/Path/To/File.txt&amp;quot;.&amp;#160; When the component is instantiated, Gallio will resolve the Uri and provide a FileInfo with the appropriate full path to the file.&lt;/p&gt;  &lt;h5&gt;Plug-in and Component Traits&lt;/h5&gt;  &lt;p&gt;&lt;em&gt;Traits&lt;/em&gt; are collections of typed properties that Gallio uses to describe plug-ins and components.&lt;/p&gt;  &lt;p&gt;At runtime, plug-in traits are represented by instances of the &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_PluginTraits.htm"&gt;PluginTraits&lt;/a&gt; class which looks a bit like this:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;public class PluginTraits : Traits     &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public PluginTraits(string name) { ... }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public string Name { get; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public string Version { get; set; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public Icon Icon { get; set; }      &lt;br /&gt;&lt;strong&gt;&amp;#160;&amp;#160;&amp;#160; // SNIP...&lt;/strong&gt;      &lt;br /&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Likewise, component traits are represented by instances of the service's associated &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_Traits.htm"&gt;Traits&lt;/a&gt; type.&amp;#160; To establish that association, a service type may have an optional &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_TraitsAttribute.htm"&gt;TraitsAttribute&lt;/a&gt; applied.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;[Traits(typeof(MyServiceTraits))]     &lt;br /&gt;public interface MyService      &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; void DoSomething();      &lt;br /&gt;}&lt;/p&gt;    &lt;p&gt;public class MyServiceTraits : Traits     &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public string SomeInformation { get; set; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public string[] MoreInformation { get; set; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public Icon MyIcon { get; set; }      &lt;br /&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Traits parameters are bound just like component parameters except that they appear in the &lt;em&gt;&amp;lt;traits&amp;gt;&lt;/em&gt; element of the ,plugin XML.&lt;/p&gt;  &lt;h5&gt;Plug-in Dependencies&lt;/h5&gt;  &lt;p&gt;In the .plugin XML, the &lt;em&gt;&amp;lt;dependencies&amp;gt;&lt;/em&gt; element specifies the ids of other plug-ins that are required by this plug-in.&lt;/p&gt;  &lt;p&gt;The dependency information allows Gallio to do a few useful things:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;If a plug-in dependency is missing or disabled then all dependent plug-ins are automatically disabled.&lt;/li&gt;    &lt;li&gt;Before activating a plug-in, all of its dependencies are activated.&amp;#160; (Note: Not implemented in v3.1 but may appear in a later version.)&lt;/li&gt;    &lt;li&gt;When verifying the plug-in installation, Gallio can check that all of the services required by a plug-in are provided by that plug-in or by one of its dependencies.&amp;#160; This check helps developers find errors in component registrations.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Assembly References and Probing Paths&lt;/h5&gt;  &lt;p&gt;Each plug-in describes the list of assemblies that it required in the &lt;em&gt;&amp;lt;assemblies&amp;gt; &lt;/em&gt;element of its plug-in XML.&amp;#160; Before attempting to use any components defined by a plug-in, Gallio first ensures that the plug-in's assemblies can be resolved.&amp;#160; Assembly references can also be qualified with binding redirects and other loader options.&lt;/p&gt;  &lt;p&gt;A plug-in can also declare custom probing paths in a &lt;em&gt;&amp;lt;probingPaths&amp;gt;&lt;/em&gt; element.&lt;/p&gt;  &lt;p&gt;Here are a few possibilities:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;lt;probingPaths&amp;gt;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;probingPath&amp;gt;customBins&amp;lt;/probingPaths&amp;gt;      &lt;br /&gt;&amp;lt;/probingPaths&amp;gt;&lt;/p&gt;    &lt;p&gt;&amp;lt;assemblies&amp;gt;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;assembly fullName=&amp;quot;MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=eb9cfa67ee6ab36e&amp;quot; codeBase=&amp;quot;MyAssembly.dll&amp;quot; qualifyPartialName=&amp;quot;true&amp;quot;&amp;#160; applyPublisherPolicy=&amp;quot;true&amp;quot;&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;bindingRedirects&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;bindingRedirect oldVersions=&amp;quot;0.0.0.0-1.1.0.0&amp;quot; newVersion=&amp;quot;2.0.0.0&amp;quot; /&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/bindingRedirects&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/assembly&amp;gt;&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;assembly fullName=&amp;quot;MyAssemblyInGAC, Version=1.0.0.0, Culture=neutral, PublicKeyToken=eb9cfa67ee6ab36e&amp;quot; /&amp;gt;     &lt;br /&gt;&amp;lt;/assemblies&amp;gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;h5&gt;Plug-in Files and Recommended Installation Path&lt;/h5&gt;  &lt;p&gt;In order to facilitate plug-in installation, each plug-in includes two pieces of information:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;A list of files in a &lt;em&gt;&amp;lt;files&amp;gt;&lt;/em&gt; element.&lt;/li&gt;    &lt;li&gt;A recommended installation path (relative to the Gallio bin directory) in the &lt;em&gt;recommendedInstallationPath&lt;/em&gt; attribute of the &lt;em&gt;&amp;lt;plugin&amp;gt;&lt;/em&gt; element.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Gallio uses this information in two ways:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;During plug-in installation it is able to determine which files belong to which plug-ins so that it can copy or delete just the necessary files.&amp;#160; (Note: Automatic plug-in installation is not implemented in Gallio v3.1 and will appear in a later release.)&lt;/li&gt;    &lt;li&gt;On request, it checks the information to verify that all plug-ins are installed as expected and that no files are missing.&amp;#160; This is part of what happens when you run the &amp;quot;Gallio.Utility.exe VerifyInstallation&amp;quot; command.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;&lt;strong&gt;Conditional Plugin Enablement&lt;/strong&gt;&lt;/h5&gt;  &lt;p&gt;Plug-ins can include a conditional expression that specifies when the plug-in should be enabled based on characteristics of the environment.&amp;#160; For example, some plug-ins related to Visual Studio integration are only enabled when Gallio is hosted by the appropriate version of Visual Studio.&lt;/p&gt;  &lt;p&gt;The conditional expression appears in the &lt;em&gt;enableCondition&lt;/em&gt; attribute of the &lt;em&gt;&amp;lt;plugin&amp;gt;&lt;/em&gt; element of the .plugin XML files.&lt;/p&gt;  &lt;p&gt;Conditions are represented in the code by &lt;a href="http://www.gallio.org/api/html/T_Gallio_Common_Condition.htm"&gt;Condition&lt;/a&gt; objects.&amp;#160; Each Condition consists of a simple boolean expression of terms drawn from a &lt;a href="http://www.gallio.org/api/html/T_Gallio_Common_ConditionContext.htm"&gt;ConditionContext&lt;/a&gt; and combined using logical &amp;quot;or&amp;quot;.&amp;#160; For plug-in enablement the runtime uses the &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_RuntimeConditionContext.htm"&gt;RuntimeConditionContext&lt;/a&gt; object to query properties in the environment.&lt;/p&gt;  &lt;p&gt;The following condition properties are currently supported.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;em&gt;&amp;quot;${env:ENVIRONMENTVARIABLE}&amp;quot;&lt;/em&gt;: Satisfied when the environment contains a variable called &amp;quot;ENVIRONMENTVARIABLE&amp;quot;. &lt;/li&gt;    &lt;li&gt;&lt;em&gt;&amp;quot;${minFramework:NET20}&amp;quot;, &amp;quot;${minFramework:NET30}&amp;quot;, &amp;quot;${minFramework:NET35}&amp;quot;, &amp;quot;${minFramework:NET40}&amp;quot;&lt;/em&gt;: Satisfied when the currently running .Net runtime version is at least the specified version. &lt;/li&gt;    &lt;li&gt;&lt;em&gt;&amp;quot;${process:PROC.EXE}&amp;quot;, &amp;quot;${process:PROC.EXE_V1}&amp;quot;, &amp;quot;${process:PROC.EXE_V1.2}&amp;quot;, &amp;quot;${process:PROC.EXE_V1.2.3}&amp;quot;, &amp;quot;${process:PROC.EXE_V1.2.3.4}&amp;quot;&lt;/em&gt;: Satisfied when the currently running process main module is &amp;quot;PROC.EXE&amp;quot; and exactly matches the specified file version components (if any).&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;For example, here's part of the .plugin XML file for the Gallio35 plug-in which provides .Net 3.5 specific extensions.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;lt;plugin pluginId=&amp;quot;Gallio35&amp;quot;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; recommendedInstallationPath=&amp;quot;&amp;quot;      &lt;br /&gt;&lt;strong&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; enableCondition=&amp;quot;${minFramework:NET35}&amp;quot;&lt;/strong&gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; xmlns=&amp;quot;&lt;a href="http://www.gallio.org/""&gt;http://www.gallio.org/&amp;quot;&lt;/a&gt;&amp;gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;Runtime API&lt;/h4&gt;  &lt;p&gt;Gallio clients mainly interact with the plug-in model by way of the following types:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_RuntimeAccessor.htm"&gt;RuntimeAccessor&lt;/a&gt;: Accessor for the core runtime objects.&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_IRuntime.htm"&gt;IRuntime&lt;/a&gt;: Configures and provides access to the plug-in registry, service locator, runtime logger, and other runtime parameters.&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_IRegistry.htm"&gt;IRegistry&lt;/a&gt;: A registry of all installed plug-ins, services and components.&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_IServiceLocator.htm"&gt;IServiceLocator&lt;/a&gt;: A service locator for resolving services and components provided by plug-ins.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;The initialization process looks a bit like this:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Client code calls &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_RuntimeBootstrap.htm"&gt;RuntimeBootstrap&lt;/a&gt;.Initialize() to initialize the runtime.&lt;/li&gt;    &lt;li&gt;The runtime creates a &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_CachingPluginLoader.htm"&gt;CachingPluginLoader&lt;/a&gt;.&lt;/li&gt;    &lt;li&gt;The caching plugin loader loads plug-in metadata into a &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_PluginCatalog.htm"&gt;PluginCatalog&lt;/a&gt;.&amp;#160; The metadata is represented in the catalog as &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_PluginRegistration.htm"&gt;PluginRegistration&lt;/a&gt;, &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_ServiceRegistration.htm"&gt;ServiceRegistration&lt;/a&gt;, and &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_ComponentRegistration.htm"&gt;ComponentRegistration&lt;/a&gt; objects.&lt;/li&gt;    &lt;li&gt;The runtime augments the catalog with a few built-in services of its own.&lt;/li&gt;    &lt;li&gt;The runtime creates a new &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_Registry.htm"&gt;Registry&lt;/a&gt; and populates it from the catalog.&amp;#160; Gallio performs many checks during this stage to ensure that plug-ins are well-formed and that all of their assemblies are accessible.&amp;#160; It automatically disables plug-ins that have problems.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Once the registry has been initialized, client code can use it in different ways.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;The registry provides methods for getting &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_IPluginDescriptor.htm"&gt;IPluginDescriptor&lt;/a&gt;, &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_IServiceDescriptor.htm"&gt;IServiceDescriptor&lt;/a&gt;, and &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_IComponentDescriptor.htm"&gt;IComponentDescriptor&lt;/a&gt; objects.&amp;#160; Descriptors are used to get information about plug-ins, services and components as well as to obtain instances of components and traits objects.&lt;/li&gt;    &lt;li&gt;The service locator provides methods for resolving services.&amp;#160; These are the same kinds of familiar operations exposed by most .Net IoCs such as &lt;a href="http://www.gallio.org/api/html/Overload_Gallio_Runtime_Extensibility_IServiceLocator_Resolve.htm"&gt;Resolve&lt;/a&gt;, and &lt;a href="http://www.gallio.org/api/html/M_Gallio_Runtime_Extensibility_IServiceLocator_ResolveByComponentId.htm"&gt;ResolveByComponentId&lt;/a&gt; along with a couple novel ones like &lt;a href="http://www.gallio.org/api/html/Overload_Gallio_Runtime_Extensibility_IServiceLocator_ResolveHandle.htm"&gt;ResolveHandle&lt;/a&gt; and &lt;a href="http://www.gallio.org/api/html/M_Gallio_Runtime_Extensibility_IServiceLocator_ResolveHandleByComponentId.htm"&gt;ResolveHandleByComponentId&lt;/a&gt; which obtain late-bound &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_ComponentHandle_2.htm"&gt;ComponentHandles.&lt;/a&gt;&amp;#160; Gallio does not use the service locator explicitly very often.&amp;#160; Most service location is performed implicitly by the runtime as part of dependency injection.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;The &lt;a href="http://www.gallio.org/api/html/T_Gallio_Runtime_Extensibility_ComponentHandle_2.htm"&gt;ComponentHandle&lt;/a&gt; type is interesting in that it allows clients to obtain a typed reference to a component descriptor without resolving the component instance itself until needed.&amp;#160; Here is an example usage of a component handle to enumerate installed components and choose to instantiate one or more of them based on some criterion.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;var frameworks = new List&amp;lt;ITestFramework&amp;gt;()     &lt;br /&gt;ComponentHandle&amp;lt;ITestFramework, TestFrameworkTraits&amp;gt;[]&amp;#160; frameworkHandles = // ...&lt;/p&gt;    &lt;p&gt;foreach (var frameworkHandle in frameworkHandles)     &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; TestFrameworkTraits traits = frameworkHandle.GetTraits();      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; if (IsSupportedFileType(traits.FileTypes, testFile))      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; frameworks.Add(frameworkHandle.GetComponent());      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;Brief Comparison with Other Plug-In Models&lt;/h4&gt;  &lt;p&gt;Gallio's plug-in model is similar to the &lt;a href="http://www.eclipse.org/"&gt;Eclipse&lt;/a&gt; plug-in model, &lt;a href="http://www.mono-project.com/Mono.Addins"&gt;Mono.Addins&lt;/a&gt; and &lt;a href="http://www.codeplex.com/MEF"&gt;MEF&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Eclipse and Mono.Addins both use XML to register plug-ins and to declare extensions points (services) and extensions (components).&amp;#160; Extensions can provide ample metadata in the form of XML extension configuration data.&amp;#160; Both of these frameworks also have provisions for packaging plug-ins as archives that can be installed incrementally.&amp;#160; (Of course, Mono.Addins was inspired by Eclipse so this is no surprise.)&lt;/p&gt;  &lt;p&gt;MEF supports late-binding Exports (component handles) and makes available ExportMetadata (traits) for clients to use.&amp;#160; MEF is mainly attribute-based but can be configured to use external metadata files by providing an appropriate Catalog implementation.&amp;#160; It is quite powerful and will be included as a standard system component in the .Net Framework 4.0 release.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Aside: The original Gallio plug-in model used the &lt;/em&gt;&lt;a href="http://www.castleproject.org/container/index.html"&gt;&lt;em&gt;Castle Windsor&lt;/em&gt;&lt;/a&gt;&lt;em&gt; inversion of control container as its foundation.&amp;#160; Unfortunately Windsor does not support late-binding (it resolves all type names to Types on initialization) and it has limited support for component metadata.&amp;#160; It is a great tool (and I highly recommend it for many projects) but just not well suited to Gallio's needs.&lt;/em&gt;&lt;/p&gt;  &lt;h5&gt;Why Not Use Mono.Addins or MEF for Gallio?&lt;/h5&gt;  &lt;p&gt;This was a tough call.&lt;/p&gt;  &lt;p&gt;I took a good long look at Mono.Addins and MEF beforehand.&amp;#160; Both are good options but I had a couple of problems with each of them.&lt;/p&gt;  &lt;p&gt;I rejected Mono.Addins because I was not confident that I would be able to morph it into the kind of shape I needed for Gallio.&amp;#160; I found the code to be tightly coupled in several places that I knew I would have to change dramatically to get the kind of dependency injection and late-binding features that I wanted.&lt;/p&gt;  &lt;p&gt;And MEF unfortunately required .Net 3.5 as a minimum whereas I had selected a target framework of .Net 2.0 for Gallio.&amp;#160; I briefly considered porting MEF to .Net 2.0 but gave up once I saw how much of System.Core it used.&amp;#160; Also it was clear that the attribute model would not work well for Gallio's late-binding needs so I would not benefit from those features.&amp;#160; Oh well.&amp;#160; I look forward to trying MEF out another day with a .Net 4.0 project perhaps.&lt;/p&gt;  &lt;p&gt;So it just seemed more straightforward to start over which would give me a maximum of flexibility in the implementation.&amp;#160; As it happens, I have taken advantage of that flexibility many times over to make things better and I can say I am very happy I did not try to shoehorn Gallio into Mono.Addins or MEF.&amp;#160; For example, some of the plug-in metadata Gallio uses would be very awkward to implement and consume with either of these libraries.&lt;/p&gt;  &lt;p&gt;YMMV.&lt;/p&gt;  &lt;h4&gt;Future Directions&lt;/h4&gt;  &lt;p&gt;Here are some things to expect from future versions of the Gallio plug-in model:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;A plug-in manager tool for downloading, installing, and maintaining plug-ins.&lt;/li&gt;    &lt;li&gt;A plug-in activation process to enable plug-ins to have more control over their early initialization.&lt;/li&gt;    &lt;li&gt;Extensible object dependency resolvers.&lt;/li&gt;    &lt;li&gt;More optimizations for start-up performance.&lt;/li&gt;    &lt;li&gt;Component scopes so that test frameworks can install special extensions while they run.&amp;#160; eg. custom object formatters for MbUnit.&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:0e348907-00d9-4810-acc4-4b2eda67c007" class="wlWriterSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/7_smBbD_YTE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/1932538451854548391/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=1932538451854548391" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/1932538451854548391?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/1932538451854548391?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/09/gallio-plug-in-model.html" title="The Gallio Plug-in Model" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;CEYMRXwyfyp7ImA9WxNRFkQ.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-7613277539553955023</id><published>2009-09-11T11:03:00.001-07:00</published><updated>2009-09-11T11:03:04.297-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-11T11:03:04.297-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="release notes" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Announcing Gallio and MbUnit v3.1!</title><content type="html">&lt;p&gt;Gallio v3.1 is a major upgrade to the platform.&amp;#160; This release includes many new features, better performance, and improved robustness.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Highlights:&lt;/em&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;em&gt;Video capture and embedding in test reports.&lt;/em&gt; &lt;/li&gt;    &lt;li&gt;&lt;em&gt;RSpec!&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;&lt;em&gt;Support for .Net Framework 4.0.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;&lt;em&gt;Support for Visual Studio 2010 Beta 1.&lt;/em&gt; &lt;/li&gt;    &lt;li&gt;&lt;em&gt;Control Panel application.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;&lt;em&gt;Brand new plug-in model with improved startup performance.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;&lt;em&gt;Major internal redesign.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;&lt;em&gt;More MbUnit goodness.&lt;/em&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Download here: &lt;a href="http://www.gallio.org/Downloads.aspx"&gt;http://www.gallio.org/Downloads.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Documentation here: &lt;a href="http://www.gallio.org/Docs.aspx"&gt;http://www.gallio.org/Docs.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Earlier release notes: &lt;a href="http://blog.bits-in-motion.com/2009/03/announcing-gallio-and-mbunit-v306.html"&gt;v3.0.6&lt;/a&gt;, &lt;a href="http://blog.bits-in-motion.com/2009/04/announcing-gallio-and-mbunit-v306.html"&gt;v3.0.6 update 1&lt;/a&gt;, &lt;a href="http://blog.bits-in-motion.com/2009/05/announcing-gallio-and-mbunit-v306.html"&gt;v3.0.6 update 2&lt;/a&gt;, &lt;a href="http://blog.bits-in-motion.com/search/label/release%20notes"&gt;all versions&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Installation Notes&lt;/h4&gt;  &lt;p&gt;We recommend completely uninstalling any previous installation of Gallio before installing the new one.&amp;#160; Be sure to shut down Visual Studio while uninstalling because some files may still be in use.&lt;/p&gt;  &lt;p&gt;We strongly recommend installing &lt;a href="https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=16827"&gt;KB963676&lt;/a&gt; before installing Gallio.&amp;#160; This patch fixes some Visual Studio crashes that may occur while editing ASP.Net pages.&lt;/p&gt;  &lt;h3&gt;New and Noteworthy&lt;/h3&gt;  &lt;h4&gt;Video Capture and Embedding&lt;/h4&gt;  &lt;p&gt;Gallio now includes support for capturing video screen recordings and embedding them in test reports.&lt;/p&gt;  &lt;h5&gt;Why?&lt;/h5&gt;  &lt;p&gt;Imagine you were writing integration tests for a Web or GUI application through the UI using a library like &lt;a href="http://watin.sourceforge.net/"&gt;WatiN&lt;/a&gt; or &lt;a href="http://www.codeplex.com/white"&gt;White&lt;/a&gt;.&amp;#160; When the test fails, it is very handy to have a record of just what happened.&lt;/p&gt;  &lt;h5&gt;Magic&lt;/h5&gt;  &lt;p&gt;In the next couple of sections I'm going to show how you can capture and embed screen recordings like the following into Gallio test reports.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px" id="scid:5737277B-5D6D-4f48-ABFC-DD9C333F4C5D:b628fcc4-7165-4a47-a5df-9e41ff2fe63a" class="wlWriterSmartContent"&gt;&lt;div&gt;&lt;object width="425" height="355"&gt;&lt;param name="movie" value="http://www.youtube.com/v/rN0CmutflFs&amp;amp;hl=en&amp;amp;fs=1&amp;amp;"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/rN0CmutflFs&amp;amp;hl=en&amp;amp;fs=1&amp;amp;" type="application/x-shockwave-flash" wmode="transparent" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h5&gt;Screen Capture and Embedding&lt;/h5&gt;  &lt;p&gt;This is all it takes to capture and embed a screenshot in the test report.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQNjxzhII/AAAAAAAAAZQ/bQ8h-gYx7oc/s1600-h/image%5B95%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQOYWotII/AAAAAAAAAZU/TvDnjKcPak4/image_thumb%5B49%5D.png?imgmax=800" width="349" height="121" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Or if we just want to capture a screenshot when a test fails or is inconclusive, here's how we can do it.&amp;#160; We can also scale the image by 25% to keep the report small while still preserving enough detail to read.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQO7GWqTI/AAAAAAAAAZY/uv_OuAVorw8/s1600-h/image%5B99%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQPen2d-I/AAAAAAAAAZc/r6mEpCzOC7w/image_thumb%5B51%5D.png?imgmax=800" width="499" height="165" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;As you can see the &lt;a href="http://www.gallio.org/api/html/AllMembers_T_Gallio_Framework_Capture.htm"&gt;Capture class&lt;/a&gt; has some interesting methods.&lt;/p&gt;  &lt;h5&gt;Creating and Embedding Videos&lt;/h5&gt;  &lt;p&gt;A video is just an encoded sequence of frames.&amp;#160; So we can build up a screen recording by taking screenshots at intervals and adding them to an instance of the &lt;a href="http://www.gallio.org/api/html/T_Gallio_Common_Media_Video.htm"&gt;Video class&lt;/a&gt; such as a &lt;a href="http://www.gallio.org/api/html/T_Gallio_Common_Media_FlashScreenVideo.htm"&gt;FlashScreenVideo&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQQLk7WpI/AAAAAAAAAZg/VXKmLZhGs_w/s1600-h/image%5B103%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQQ9gRD_I/AAAAAAAAAZk/RKOsooSgPzI/image_thumb%5B53%5D.png?imgmax=800" width="479" height="310" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Notice that you can create videos of anything you like.&amp;#160; You are not limited to screen recordings.&lt;/p&gt;  &lt;h5&gt;Screen Recording and Embedding&lt;/h5&gt;  &lt;p&gt;Gallio offers a few shortcuts to simplify screen recording in particular.&lt;/p&gt;  &lt;p&gt;The &lt;a href="http://www.gallio.org/api/html/T_Gallio_Common_Media_ScreenRecorder.htm"&gt;ScreenRecorder class&lt;/a&gt; captures screenshots in a background thread and assembles them into a Video that can later be embedded.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQRZEIX0I/AAAAAAAAAZo/uNetmZTD2c0/s1600-h/image%5B107%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQSHrRxrI/AAAAAAAAAZs/AI7hMZIYNq0/image_thumb%5B55%5D.png?imgmax=800" width="466" height="186" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The &lt;a href="http://www.gallio.org/api/html/T_Gallio_Framework_Capture.htm"&gt;Capture class&lt;/a&gt; also provides a method for automatically embedding a screen recording based on the outcome of the test.&amp;#160; It starts a screen recorder in the background and when the test finishes decides whether to embed the video or discard it.&lt;/p&gt;  &lt;p&gt;This is a very powerful one-liner...&lt;/p&gt;  &lt;p&gt;Here we set things up to capture a screen recording at 5 frames per second and 25% zoom factor and cause it to be automatically embedded if the test fails or is inconclusive.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQS9hLyrI/AAAAAAAAAZw/K8nAr3sIxeQ/s1600-h/image%5B111%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQTg8pPGI/AAAAAAAAAZ0/dRRAKXS-cIs/image_thumb%5B57%5D.png?imgmax=800" width="539" height="152" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;Overlays&lt;/h5&gt;  &lt;p&gt;Raw screen recordings are very useful but we can enrich them with additional information.&amp;#160; For example, while playing back the recording, it can be useful to see a transcript of what the system was doing when it captured the frame.&lt;/p&gt;  &lt;p&gt;Gallio supports applying any number of &lt;a href="http://www.gallio.org/api/html/T_Gallio_Common_Media_Overlay.htm"&gt;Overlays&lt;/a&gt; to any screen recording.&amp;#160; An overlay is a basically an object that paints itself onto an image.&lt;/p&gt;  &lt;p&gt;We're going to use a &lt;a href="http://www.gallio.org/api/html/T_Gallio_Common_Media_CaptionOverlay.htm"&gt;CaptionOverlay&lt;/a&gt; to add a caption to the video as we record it.&amp;#160; We can change the caption between frames to display a variety of information.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQVCDSp3I/AAAAAAAAAZ4/6ZlmAJfu0OE/s1600-h/image%5B115%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQV5V6AGI/AAAAAAAAAZ8/i5bhkZSu2aQ/image_thumb%5B59%5D.png?imgmax=800" width="473" height="393" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;That's a lot of work so the &lt;a href="http://www.gallio.org/api/html/T_Gallio_Framework_Capture.htm"&gt;Capture class&lt;/a&gt; provides a few convenience methods for setting a caption overlay for its screen captures and screen recordings.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQWs6mj1I/AAAAAAAAAaA/hnsbS8KdXeg/s1600-h/image%5B119%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQXs3QXvI/AAAAAAAAAaE/ALOSRJVosPM/image_thumb%5B61%5D.png?imgmax=800" width="615" height="314" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;Magic Recipe for WatiN&lt;/h5&gt;  &lt;p&gt;Here's the whole recipe for hooking up automatic screen recordings with WatiN 2.0.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;public abstract class WatiNFixture     &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; protected Browser Browser { get; private set; } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; [SetUp]     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public void SetUp()      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Logger.LogWriter = new GallioLogger(); &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Capture.SetCaptionAlignment(HorizontalAlignment.Center, VerticalAlignment.Bottom);     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Capture.SetCaptionFontSize(32);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Capture.AutoEmbedRecording(TriggerEvent.TestFailedOrInconclusive,      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;quot;Screen Recording&amp;quot;,      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; new CaptureParameters() { Zoom = 0.25 }, 5 /*frames per second*/); &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; IE ie = new IE();     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ie.ShowWindow(NativeMethods.WindowShowStyle.ShowMaximized);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Browser = ie;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; [TearDown]     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public void TearDown()      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Capture.SetCaption(&amp;quot;&amp;quot;); &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (Browser != null)     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Browser.Close();      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Browser = null;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; private sealed class GallioLogger : ILogWriter     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; public void LogAction(string message)      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Capture.SetCaption(message);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; TestLog.WriteLine(message);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; public void LogInfo(string message)     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; TestLog.WriteLine(message);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; public void LogDebug(string message)     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // Ignore these messages.      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;} &lt;/p&gt;    &lt;p&gt;public class SampleTest : WatiNFixture     &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; [Test]      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public void GoogleSearchAndVisitMaps()      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Browser.GoTo(&amp;quot;http://www.google.com&amp;quot;);&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Browser.TextField(Find.ByName(&amp;quot;q&amp;quot;)).TypeText(&amp;quot;Ottawa&amp;quot;);     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Browser.Button(Find.ByName(&amp;quot;btnG&amp;quot;)).Click(); &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Browser.Link(Find.ByText(&amp;quot;Ottawa, ON Canada&amp;quot;)).Click(); &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Browser.Link(Find.ByText(&amp;quot;Parliament Hill&amp;quot;)).Click(); &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Browser.Link(Find.ByText(&amp;quot;Full article&amp;quot;)).Click(); &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Assert.Fail(&amp;quot;Just for show.&amp;quot;);     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;} &lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;RSpec and IronRuby&lt;/h4&gt;  &lt;p&gt;Now for something completely different: RSpec integration.&lt;/p&gt;  &lt;h5&gt;Big News&lt;/h5&gt;  &lt;p&gt;The big news here is that Gallio now supports running tests that are defined in files instead of just .Net test assemblies.&amp;#160; This means you can pretty easily create custom test framework adapters for any kind of file that you know how to process and then run those tests using Icarus and other tools.&lt;/p&gt;  &lt;p&gt;Yes, that means you can implement 5 methods of ITestDriver (or one of the helper subclasses Gallio provides) and have a custom test framework of your own using whatever kinds test files you like be they spreadsheets, XML files, native executables, or programming language text.&lt;/p&gt;  &lt;h5&gt;RSpec in Action&lt;/h5&gt;  &lt;p&gt;Let's suppose we have implemented a &amp;quot;Bowling&amp;quot; class that computes the score of a bowling game.&amp;#160; (This example is borrowed from the &lt;a href="http://rspec.info/"&gt;RSpec site&lt;/a&gt;.)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;class Bowling     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; def hit(pins)      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; end&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; def score     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 0      &lt;br /&gt;&amp;#160; end      &lt;br /&gt;end&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Obviously this implementation will not get us very far.&amp;#160; So we write some &lt;a href="http://en.wikipedia.org/wiki/Behavior_Driven_Development"&gt;specs&lt;/a&gt; to guide us along.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;require 'bowling'&lt;/p&gt;    &lt;p&gt;describe Bowling do     &lt;br /&gt;&amp;#160; it &amp;quot;should score 0 for gutter game&amp;quot; do      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; bowling = Bowling.new&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; 20.times { bowling.hit(0) } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; bowling.score.should == 0     &lt;br /&gt;&amp;#160; end &lt;/p&gt;    &lt;p&gt;&amp;#160; it &amp;quot;should score 300 for perfect game&amp;quot; do     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; bowling = Bowling.new &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; 12.times { bowling.hit(10) } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; bowling.score.should == 300     &lt;br /&gt;&amp;#160; end &lt;/p&gt;    &lt;p&gt;&amp;#160; it &amp;quot;should score 20 for single pin hit each ball&amp;quot; do     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; pending(&amp;quot;Scoring to be implemented.&amp;quot;)      &lt;br /&gt;&amp;#160; end       &lt;br /&gt;end&lt;/p&gt; &lt;/blockquote&gt;  &lt;h5&gt;RSpec + Icarus&lt;/h5&gt;  &lt;p&gt;Now we just add the bowling_spec.rb file to Icarus, and run...&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQY5Ns7FI/AAAAAAAAAaI/cmR4CjecbD0/s1600-h/image%5B87%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQaZWRXPI/AAAAAAAAAaM/vYPB-1qdItk/image_thumb%5B45%5D.png?imgmax=800" width="784" height="558" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h5&gt;RSpec + Icarus + AutoRun = Bliss&lt;/h5&gt;  &lt;p&gt;When programming in a BDD way you will probably find it useful to enable the Icarus auto-run feature.&amp;#160; Then whenever you edit the spec, they will be reloaded and re-executed.&lt;/p&gt;  &lt;p&gt;It's a little slow right now but expect it to get better in future versions.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQbJ0JGnI/AAAAAAAAAaQ/Ukl-1W0tjwo/s1600-h/image%5B91%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQbwOxlJI/AAAAAAAAAaU/OfhAOuWtW94/image_thumb%5B47%5D.png?imgmax=800" width="512" height="471" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h4&gt;MbUnit Features&lt;/h4&gt;  &lt;p&gt;Yann Tr&amp;#233;vin has been busy cooking up new features for MbUnit v3.1.&lt;/p&gt;  &lt;h5&gt;Retry.Until&lt;/h5&gt;  &lt;p&gt;The Retry class makes it easy to implement polling loops in tests.&amp;#160; Polling loops are useful when the test must wait for an asynchronous operation to complete but does not know how long it might take.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQcgP_igI/AAAAAAAAAaY/9xL4SN7Q6Bs/s1600-h/image%5B48%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQdZBSZFI/AAAAAAAAAac/jedD9A-4D9M/image_thumb%5B26%5D.png?imgmax=800" width="481" height="154" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://interfacingreality.blogspot.com/2009/05/retryuntil-in-mbunit-v3.html"&gt;More information about Retry.Until.&lt;/a&gt;&lt;/p&gt;  &lt;h5&gt;Assert.Sorted and Assert.Distinct&lt;/h5&gt;  &lt;p&gt;Two new assertions for checking whether the elements in a collection are sorted or are distinct.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://interfacingreality.blogspot.com/2009/07/assertsorted-in-mbunit-v3.html"&gt;More information about Assert.Sorted.&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://interfacingreality.blogspot.com/2009/06/assertdistinct-in-mbunit-v3.html"&gt;More information about Assert.Distinct.&lt;/a&gt;&lt;/p&gt;  &lt;h5&gt;Assert.ForAll and Assert.Exists&lt;/h5&gt;  &lt;p&gt;Two new assertions for checking whether a property holds true for all elements of a collection or for at least one of them.&amp;#160; These assertions are incredibly useful.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQdwhxgoI/AAAAAAAAAag/07ykn495ctU/s1600-h/image%5B52%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQepiFPwI/AAAAAAAAAak/ylTauyBwkps/image_thumb%5B28%5D.png?imgmax=800" width="358" height="122" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://interfacingreality.blogspot.com/2009/08/assertforall-and-assertexists-in-mbunit.html"&gt;More information about Assert.ForAll and Assert.Exists.&lt;/a&gt;&lt;/p&gt;  &lt;h5&gt;Assert.Throws for Inner Exceptions&lt;/h5&gt;  &lt;p&gt;Assert.Throws now accepts an optional second type parameter to specify an inner exception type.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://interfacingreality.blogspot.com/2009/04/asserting-inner-exception.html"&gt;More information about Assert.Throws in general.&lt;/a&gt;&lt;/p&gt;  &lt;h5&gt;StructuralEqualityComparer&lt;/h5&gt;  &lt;p&gt;Did you ever have a data structure that you wanted to check for equality but it did not override Object.Equals?&amp;#160; Ever grumble about having to implement this check manually?&lt;/p&gt;  &lt;p&gt;MbUnit v3.1 includes a new feature called a structural equality comparer.&amp;#160; The idea is to make it super easy to compare all members of a structure according to some rule.&amp;#160; Basically you just provide a list of lambda expressions to select structure members to compare and then you optionally provide a comparison predicate.&amp;#160; MbUnit will then compare the structure member by member using the provided selectors and predicates.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQfc63VlI/AAAAAAAAAao/euiGta3d8VQ/s1600-h/image%5B56%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQgNk7VGI/AAAAAAAAAas/dwnAKBRA8X0/image_thumb%5B30%5D.png?imgmax=800" width="564" height="139" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://interfacingreality.blogspot.com/2009/09/equality-assertions-in-mbunit-v3.html"&gt;More information about StructuralEqualityComparer.&lt;/a&gt;&lt;/p&gt;  &lt;h5&gt;EnumData, SequentialNumbers, RandomNumbers and RandomStrings&lt;/h5&gt;  &lt;p&gt;Yann wrote a bunch more nifty data source attributes.&amp;#160; Here they are in one big combinatorial test.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQg3C7gJI/AAAAAAAAAaw/Ocr5BeQW6w4/s1600-h/image%5B63%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQhnTEuBI/AAAAAAAAAa0/D6sO-1qVaDM/image_thumb%5B33%5D.png?imgmax=800" width="672" height="152" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;[MultipleCulture]&lt;/h5&gt;  &lt;p&gt;The [MultipleCulture] attribute runs a test repeatedly with different cultures.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQiDHpy9I/AAAAAAAAAa4/-ti1Zw2ywek/s1600-h/image%5B79%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQi1UJIII/AAAAAAAAAa8/16i-BWM1Jts/image_thumb%5B41%5D.png?imgmax=800" width="259" height="93" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;[Impersonate]&lt;/h5&gt;  &lt;p&gt;The [Impersonate] attribute runs a test using different user credentials.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQjNge-JI/AAAAAAAAAbA/Nm8MPuh73Co/s1600-h/image%5B83%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQj6_92xI/AAAAAAAAAbE/aAQ3yTFgu0U/image_thumb%5B43%5D.png?imgmax=800" width="389" height="92" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;TestContext.AutoExecute&lt;/h5&gt;  &lt;p&gt;TestContext.AutoExecute makes it easy to register actions to perform when a test passes or fails without needing to write a [TearDown] method.&lt;/p&gt;  &lt;p&gt;This example captures and embeds a screenshot in the test report whenever the test fails.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQkQX6VsI/AAAAAAAAAbI/NEbodSOTc7E/s1600-h/image%5B71%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQkwxtZlI/AAAAAAAAAbM/_e24TM2scuc/image_thumb%5B37%5D.png?imgmax=800" width="524" height="172" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h4&gt;Control Panel&lt;/h4&gt;  &lt;p&gt;Gallio now has a control panel application where you can set all sorts of preferences.&lt;/p&gt;  &lt;p&gt;Feel free to suggest other things for us to add to the control panel.&lt;/p&gt;  &lt;h5&gt;Gallio Runtime Settings&lt;/h5&gt;  &lt;p&gt;The Gallio runtime settings let you tweak some global parameters.&amp;#160; Currently it lets you set custom plug-in paths in case you have installed other Gallio plug-ins elsewhere in your system.&lt;/p&gt;  &lt;p&gt;Expect this list to grow...&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQll8-hCI/AAAAAAAAAbQ/YaoZBNmHuZs/s1600-h/image%5B33%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQmqmrhJI/AAAAAAAAAbU/2szJTOvgTEw/image_thumb%5B19%5D.png?imgmax=800" width="512" height="473" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;Icarus Settings&lt;/h5&gt;  &lt;p&gt;All of the Icarus settings have moved to the Control Panel so you can configure them in the same place as other settings.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQnSC3zbI/AAAAAAAAAbY/fRM0hWH4Gek/s1600-h/image%5B29%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQofQ9X4I/AAAAAAAAAbc/QAte2Lp7AW4/image_thumb%5B17%5D.png?imgmax=800" width="512" height="473" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;TestDriven.Net Settings&lt;/h5&gt;  &lt;p&gt;Now we have a place to configure settings for TestDriven.Net.&lt;/p&gt;  &lt;p&gt;The Frameworks page lets you configure whether Gallio will be the default or preferred test runner for different frameworks.&amp;#160; For example if you have &lt;a href="http://www.csunit.org/"&gt;csUnit&lt;/a&gt; installed and you prefer to use Gallio to run csUnit tests with TestDriven.Net then you can set Gallio's priority to &amp;quot;Preferred&amp;quot;.&amp;#160; If you set Gallio's priority to &amp;quot;Default&amp;quot; then Gallio will only be used if there is no other installed test runner available for a particular framework.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQrZ5JUeI/AAAAAAAAAbg/O30rKxLfrxY/s1600-h/image%5B37%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQs3pVChI/AAAAAAAAAbk/-6SZqx6Zse4/image_thumb%5B21%5D.png?imgmax=800" width="512" height="473" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;AutoCAD Settings&lt;/h5&gt;  &lt;p&gt;Mike Sandberg contributed a new preference panel for configuring the AutoCAD integration.&lt;/p&gt;  &lt;p&gt;The AutoCAD integration still supports the &amp;quot;AcadAttachToExisting&amp;quot; and &amp;quot;AcadExePath&amp;quot; runner properties but having a GUI is certainly much nicer...&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQtxV4GZI/AAAAAAAAAbo/yGJlMKY-j_E/s1600-h/image%5B25%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQunpEn-I/AAAAAAAAAbs/m6Ez69S76MM/image_thumb%5B15%5D.png?imgmax=800" width="512" height="473" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;Plugin Diagnostics&lt;/h5&gt;  &lt;p&gt;Just for completeness, Gallio also displays a list of all installed plug-ins.&lt;/p&gt;  &lt;p&gt;(Later on we will be adding a proper plug-in manager with the ability to download and install plug-ins separately.)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQvXdYCFI/AAAAAAAAAbw/S8wwAD31LsQ/s1600-h/image%5B44%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQwFYL7XI/AAAAAAAAAb0/Ney3G2uGWxc/image_thumb%5B24%5D.png?imgmax=800" width="512" height="473" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h4&gt;Visual Studio Test Tools Enhancements&lt;/h4&gt;  &lt;h5&gt;Visual Studio 2010 Beta 1&lt;/h5&gt;  &lt;p&gt;Gallio v3.1 now includes support for Visual Studio 2010 Beta 1 out of the box.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_X_hvY6E0egY/SqqQxrtRK9I/AAAAAAAAAb4/k-_wSThWiRQ/s1600-h/image%5B20%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQycAMlWI/AAAAAAAAAb8/JEkORIa2iQk/image_thumb%5B12%5D.png?imgmax=800" width="244" height="195" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;Data-Driven Test Results (Fixed)&lt;/h5&gt;  &lt;p&gt;Gallio now correctly displays the results of data-driven tests like MbUnit row tests.&lt;/p&gt;  &lt;p&gt;In the following screenshot you can clearly see both test results of a data-driven test.&amp;#160; Previous versions of Gallio would only show one result.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQzSl32RI/AAAAAAAAAcA/o6cEEDrZH5Y/s1600-h/image%5B21%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQ0FeKpsI/AAAAAAAAAcE/IwBEmvMeRS0/image_thumb%5B13%5D.png?imgmax=800" width="234" height="244" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h4&gt;AutoCAD Integration&lt;/h4&gt;  &lt;p&gt;In addition to the new preference panel, we have made a couple of minor enhancements to the AutoCAD testing experience.&lt;/p&gt;  &lt;h5&gt;Debugging AutoCAD Tests&lt;/h5&gt;  &lt;p&gt;With Gallio v3.1, you can now debug your AutoCAD tests using Icarus just like any other tests.&lt;/p&gt;  &lt;p&gt;Previously debugging did not work because the Visual Studio debugger is unable to attach to a process that uses fibers (like AutoCAD does).&amp;#160; So Gallio v3.1 launches AutoCAD with the debugger attached from the start and it works.&lt;/p&gt;  &lt;p&gt;We also support zero-installation execution of AutoCAD tests.&amp;#160; This means you can run tests on a machine with AutoCAD installed without having to separately install Gallio as long as the files have been copied to the local disk somewhere.&lt;/p&gt;  &lt;h4&gt;TeamCity Integration&lt;/h4&gt;  &lt;p&gt;Gallio v3.1 now automatically detects when it is being run by TeamCity and enables the TeamCity test runner extension.&amp;#160; This means you &lt;strong&gt;do not need &lt;/strong&gt;the following in your build scripts any more: &amp;quot;TeamCityExtension,Gallio.TeamCityIntegration&amp;quot;.&lt;/p&gt;  &lt;p&gt;There have also been some fixes to more correctly output test results from parallelizable tests.&lt;/p&gt;  &lt;h4&gt;Performance&lt;/h4&gt;  &lt;p&gt;We have invested some time improving the performance of Gallio and MbUnit v3.1 in several key areas.&lt;/p&gt;  &lt;h5&gt;&lt;/h5&gt;  &lt;h5&gt;Startup Time&lt;/h5&gt;  &lt;p&gt;Gallio v3.1 is much faster to start up than previous versions of Gallio.&lt;/p&gt;  &lt;p&gt;Much of this gain comes from three important changes.&amp;#160; First, the new plug-in model in Gallio exposes more declarative metadata up front so that we can defer loading assemblies until they are actually needed.&amp;#160; Second, once we have identified all of the plug-ins that are installed, we cache the composite metadata for subsequent runs.&amp;#160; Third, we generate XmlSerializers ahead of time.&lt;/p&gt;  &lt;p&gt;On my slow laptop Icarus startup time dropped from over 20 seconds to about 2 seconds.&amp;#160; These startup time improvements also carry over to the Visual Studio and ReSharper integration.&lt;/p&gt;  &lt;h5&gt;Memory Usage&lt;/h5&gt;  &lt;p&gt;Gallio v3.1 uses significantly less memory when running multiple test assemblies than it did in v3.0.6.&lt;/p&gt;  &lt;p&gt;Older versions of Gallio used to keep all of the test assemblies loaded in memory for the duration of the test run.&amp;#160; When running dozens of test assemblies together at once (say, during a continuous integration build) Gallio would end up consuming quite a lot of memory in proportion to the number of test assemblies.&lt;/p&gt;  &lt;p&gt;Now Gallio v3.1 loads and unloads each test assembly before moving on to the next one.&amp;#160; This change improves responsiveness and keeps memory usage in check.&lt;/p&gt;  &lt;h5&gt;Debugging and Profiling&lt;/h5&gt;  &lt;p&gt;One of the more perplexing performance problems we addressed in MbUnit v3.1 was the performance of the debugger.&lt;/p&gt;  &lt;p&gt;In older versions of MbUnit v3 it would sometimes take half a second or more to single-step while running a test in the debugger.&amp;#160; The problem is that the stack was just too deep.&amp;#160; During a typical test run, the stack could be 160 frames deep!&lt;/p&gt;  &lt;p&gt;Now MbUnit v3.1 is more carefully tuned to minimize its stack depth.&amp;#160; We have reduced the effective stack depth by a factor of &lt;strong&gt;5&lt;/strong&gt; so single-stepping is zippy and responsive just like it should be.&lt;/p&gt;  &lt;p&gt;One happy side-benefit of reduced stack depth is that it significantly easier to figure out what it going on when running tests with a code profiler.&lt;/p&gt;  &lt;h4&gt;.Net 4.0 Support and x86/x64 Processor Architecture Enhancements&lt;/h4&gt;  &lt;p&gt;Gallio v3.1 includes out of the box support for .Net 4.0.&amp;#160; We also improved our support for running tests in different processor architectures.&lt;/p&gt;  &lt;p&gt;One key change is that Gallio runs each test assembly in its own process by default.&amp;#160; Gallio automatically detects the required .Net runtime version and processor architecture by examining the assembly metadata.&amp;#160; You can run tests designed for different .Net runtimes or processors architectures side-by-side if you like.&amp;#160; All you need to do is compile your test assemblies with the right options.&lt;/p&gt;  &lt;p&gt;It just works.&lt;/p&gt;  &lt;h4&gt;Robustness&lt;/h4&gt;  &lt;p&gt;We have worked hard to improve the robustness of Gallio v3.1 while adding new features.&lt;/p&gt;  &lt;h5&gt;UAC Privilege Elevation on Vista and Windows 7&lt;/h5&gt;  &lt;p&gt;Did you notice that the Control Panel application displayed the UAC shield icon?&amp;#160; Here it is again:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/SqqQ0gRyhXI/AAAAAAAAAcI/qJQYyKS78dQ/s1600-h/image%5B40%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SqqQ1Ahq8eI/AAAAAAAAAcM/PaWNAlk8TOo/image_thumb%5B22%5D.png?imgmax=800" width="79" height="27" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The shield indicates that Gallio has detected that the Control Panel is running in a lower privilege environment and that certain settings that have changed require elevation.&amp;#160; Of course when you click on the button you will receive a UAC prompt after which Gallio will carry out the required operation in an elevated context.&lt;/p&gt;  &lt;h5&gt;ReSharper Integration Bug Fixes&lt;/h5&gt;  &lt;p&gt;We have spent some time improving the robustness of the ReSharper integration.&amp;#160; It used to throw up annoying exceptions here and there and occasionally would crash.&amp;#160; Hopefully this is all a thing of the past.&lt;/p&gt;  &lt;p&gt;Here's some bad code that used to cause Visual Studio to crash but not anymore:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;public class A : B     &lt;br /&gt;{      &lt;br /&gt;} &lt;/p&gt;    &lt;p&gt;public class B : A     &lt;br /&gt;{      &lt;br /&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;Gallio.Utility Power Tool&lt;/h4&gt;  &lt;p&gt;Gallio.Utility.exe is a new command-line application included with Gallio to perform various functions related to maintaining a Gallio installation.&lt;/p&gt;  &lt;p&gt;These are the supported utility commands at this time:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;ClearCurrentUserPluginCache:&amp;#160; Clears the plugin cache of the current user.&lt;/li&gt;    &lt;li&gt;ResetInstallationId:&amp;#160; Resets the installation id.&amp;#160; The plugin list will be refreshed the next time a Gallio application is started.&lt;/li&gt;    &lt;li&gt;Setup:&amp;#160; Installs or uninstalls components.&lt;/li&gt;    &lt;li&gt;VerifyInstallation:&amp;#160; Checks for runtime installation errors.&lt;/li&gt; &lt;/ul&gt;  &lt;h4&gt;More Goodies&lt;/h4&gt;  &lt;p&gt;MbUnit:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;MbUnit assertions offer EqualityComparison&amp;lt;T&amp;gt; and IEqualityComparer&amp;lt;T&amp;gt; overloads.&lt;/li&gt;    &lt;li&gt;Parallelizable tests run with the correct number of threads and start up immediately without requiring any prior &amp;quot;warm-up&amp;quot; period as was seen in earlier versions due to some bad logic.&lt;/li&gt;    &lt;li&gt;Support the use of Assert.Inconclusive and Assert.Terminate within Assert.Multiple blocks.&lt;/li&gt;    &lt;li&gt;Contract verifiers can be &amp;quot;static&amp;quot; in which case they behave like static test factories instead of like dynamic test factories.&amp;#160; In a &amp;quot;static&amp;quot; contract verifier, each individual verification will appear as a separate test in the test runner.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;csUnit:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Upgraded to csUnit v2.6.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;NUnit:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Upgraded to NUnit v2.5.2 but retained support for NUnit v2.4.8 also.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;MSTest:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Rewrote most of the MSTest integration to be faster and more robust.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Command-line tools:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Gallio.Echo.exe supports wildcards.&lt;/li&gt;    &lt;li&gt;The NAnt, MSBuild and PowerShell tasks support specifying the Verbosity.&lt;/li&gt;    &lt;li&gt;Echo support a /no-progress switch to disable progress reporting.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Icarus:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Icarus now has a &amp;quot;Recent Projects&amp;quot; menu.&lt;/li&gt;    &lt;li&gt;Icarus test tree is a bit faster.&lt;/li&gt;    &lt;li&gt;Lots of Icarus bug fixes.&lt;/li&gt;    &lt;li&gt;New error dialog.&lt;/li&gt;    &lt;li&gt;Support keyboard navigation.&lt;/li&gt;    &lt;li&gt;Disabled the watchdog timer during debugging runs so you can debug at leisure without worrying about the test runner killing the process prematurely.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Test Reports:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Support embedded HTML content and Flash videos.&lt;/li&gt;    &lt;li&gt;Workaround issues when clicking links to attachments in IE due to the Local Machine Zone Lockdown.&lt;/li&gt;    &lt;li&gt;Display test kind icons in the report.&lt;/li&gt;    &lt;li&gt;Added a condensed text report type.&lt;/li&gt;    &lt;li&gt;Test output is normalized before writing it to the log.&amp;#160; This is to catch problems like attempting to write a null character or BOM into the report.&lt;/li&gt;    &lt;li&gt;Test framework nodes are no longer emitted.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Debugging and Code Coverage:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Gallio launches the debugger with the &amp;quot;Managed and Native&amp;quot; debugging engine selected so that you can debug into native code from managed code.&lt;/li&gt;    &lt;li&gt;NCover v1.5.8 works on x64 but only supports running tests in x86 mode.&lt;/li&gt;    &lt;li&gt;Using NCoverExplorer or NCover.Reporting to merge NCover coverage reports when running test assemblies in separate processes.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;ReSharper:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Lots of bugfixes.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;TestDriven.Net:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Gallio now supports zero-installation TestDriven.Net test runs with recent beta builds of TestDriven.Net.&amp;#160; I expect Jamie will say more about this feature soon...&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Pex:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;MbUnit.Pex no longer included.&amp;#160; The Pex project has taken over the maintenance of this extension.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;CruiseControl.Net:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Added support for CruiseControl.Net v1.4.4 and more recent versions.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Documentation:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Yann performed a massive documentation sweep to clean up formatting.&lt;/li&gt;    &lt;li&gt;Switched to Sandcastle instead of NDoc 2 Alpha.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Core: &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Brand new test framework extension API.&amp;#160; Only 5 methods to implement.&lt;/li&gt;    &lt;li&gt;Early Transition to a message-based protocol for test framework integration.&lt;/li&gt;    &lt;li&gt;Massive refactoring of namespaces and interfaces.&lt;/li&gt; &lt;/ul&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:1e9ddcdc-41a2-4098-a1cd-8272ccf8cda8" class="wlWriterSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;,&lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/SpY44G-k5Nc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/7613277539553955023/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=7613277539553955023" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/7613277539553955023?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/7613277539553955023?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/09/announcing-gallio-and-mbunit-v31.html" title="Announcing Gallio and MbUnit v3.1!" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/_X_hvY6E0egY/SqqQOYWotII/AAAAAAAAAZU/TvDnjKcPak4/s72-c/image_thumb%5B49%5D.png?imgmax=800" height="72" width="72" /><thr:total>6</thr:total></entry><entry gd:etag="W/&quot;C0QBRXozcCp7ImA9WxJbGUk.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-4994658411279081723</id><published>2009-07-30T01:09:00.001-07:00</published><updated>2009-07-30T01:09:14.488-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-30T01:09:14.488-07:00</app:edited><title>Toward New Horizons</title><content type="html">&lt;p&gt;The short story is that I am now between jobs. &lt;/p&gt;  &lt;p&gt;The long story is that I have left Ingenio / Yellowpages.com / AT&amp;amp;T Interactive and will be joining Google in late September.&amp;#160; Yes that's the same Google I turned down last year; my priorities and perspective have changed. &lt;/p&gt;  &lt;p&gt;I am looking forward to living in Mountain View biking distance from work and walking distance to yuppie downtown amenities.&amp;#160; I also look forward to joining any number of Google's social and volunteer organizations.&amp;#160; I have pretty much decided that, as soon as I figure out how, I will find a way to teach the fine art (and joy) of programming and computer science to children, adult students and peers. &lt;/p&gt;  &lt;p&gt;Now I have two months break!&amp;#160; Feels a bit like summer vacation.&amp;#160; Nothing planned but possibilities and opportunities abound everywhere.&amp;#160; I'll probably sing, play music, learn pastels and paint, work on Open Source, take a dip in a warm ocean someplace, and relax.&lt;/p&gt;  &lt;p&gt;My last day at my old job was Friday the 24th so I am still getting over the early morning nagging sensation that I have to be someplace.&amp;#160; I'm sure that my coming week at &lt;a href="http://www.larkcamp.com/"&gt;Lark Camp&lt;/a&gt; in Mendocino will fix that.&amp;#160; :-)&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/9ufD7NzKx3I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/4994658411279081723/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=4994658411279081723" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/4994658411279081723?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/4994658411279081723?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/07/toward-new-horizons.html" title="Toward New Horizons" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>6</thr:total></entry><entry gd:etag="W/&quot;DUUNR3cycSp7ImA9WxJUGUU.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-5824862023350289587</id><published>2009-07-19T00:08:00.001-07:00</published><updated>2009-07-19T00:08:16.999-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-19T00:08:16.999-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Coming Soon: Gallio and MbUnit v3.1</title><content type="html">&lt;p&gt;The next release of Gallio and MbUnit is just around the corner.&amp;#160; I need to work on a proper write-up of all of the new stuff but here are a few highlights:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Significantly faster start-up time.&lt;/li&gt;    &lt;li&gt;Embedded videos in test reports including built-in screen recording.&amp;#160; (Try playing with the &lt;a href="http://www.gallio.org/api/html/T_Gallio_Framework_Capture.htm"&gt;Capture&lt;/a&gt; class.)&lt;/li&gt;    &lt;li&gt;Gallio can run RSpec tests via IronRuby.&lt;/li&gt;    &lt;li&gt;New Control Panel application for tweaking global settings.&lt;/li&gt;    &lt;li&gt;More MbUnit assertions and attributes for manipulating data and the environment.&lt;/li&gt;    &lt;li&gt;More Icarus GUI goodness.&lt;/li&gt;    &lt;li&gt;Better API documentation.&lt;/li&gt;    &lt;li&gt;Much simpler story for test framework adapters and test runners.&lt;/li&gt;    &lt;li&gt;Gallio can run tests in arbitrary files including tests written in non .Net languages given a suitable framework adapter (which you could write in a day).&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;The final release of v3.1 will probably happen within the next two weeks.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;We're looking for beta testers.&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;To help us out, try out one of the recent v3.1 builds from: &lt;a href="http://ccnet.gallio.org/Distributables/"&gt;http://ccnet.gallio.org/Distributables/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Please report any suggestions or issues you find here: &lt;a href="http://code.google.com/p/mb-unit/issues/list"&gt;http://code.google.com/p/mb-unit/issues/list&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You can also join our developer mailing-list: &lt;a title="http://groups.google.com/group/gallio-dev" href="http://groups.google.com/group/gallio-dev"&gt;http://groups.google.com/group/gallio-dev&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;API documentation: &lt;a title="http://www.gallio.org/api/index.aspx" href="http://www.gallio.org/api/index.aspx"&gt;http://www.gallio.org/api/index.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Thanks!&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:f0976044-35e4-4d4b-9b06-4bf0e89a39b9" class="wlWriterSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;,&lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/yNbnyO4gSM0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/5824862023350289587/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=5824862023350289587" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5824862023350289587?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5824862023350289587?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/07/coming-soon-gallio-and-mbunit-v31.html" title="Coming Soon: Gallio and MbUnit v3.1" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;C0IDRnsyeip7ImA9WxJXEUQ.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-5694751990382832716</id><published>2009-06-05T01:46:00.001-07:00</published><updated>2009-06-05T01:46:17.592-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-05T01:46:17.592-07:00</app:edited><title>Upcoming Events</title><content type="html">&lt;p&gt;I will be attending &lt;a href="http://altnetconfcanada.com/home/index.castle"&gt;ALT.Net Canada&lt;/a&gt; in Vancouver, BC from June 12th through 14th.&amp;#160; My plan is to follow that up with a week-long vacation and visiting friends and family in Ottawa, Toronto, Montreal or Kitchener-Waterloo.&lt;/p&gt;  &lt;p&gt;I rather desperately need a vacation...&lt;/p&gt;  &lt;p&gt;As for &lt;a href="http://www.gallio.org/"&gt;Gallio&lt;/a&gt;, I am currently investing most of my spare time into adding support for test files that are not .Net assemblies.&amp;#160; When I am finished, Gallio will be able to run tests written in other languages such as C++, Ruby, various XML-based DSLs or anything else that is represented as a file of a type for which a test framework plugin has been installed.&amp;#160; As a proof of concept Gallio v3.0.7 will include support for &lt;a href="http://rspec.info/"&gt;RSpec&lt;/a&gt; by way of the &lt;a href="http://www.codeplex.com/dlr"&gt;Dynamic Language Runtime&lt;/a&gt;.&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/smFNjEKgzaM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/5694751990382832716/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=5694751990382832716" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5694751990382832716?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5694751990382832716?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/06/upcoming-events.html" title="Upcoming Events" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;CkQEQ3o7fyp7ImA9WxJQFkk.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-1502699045126541716</id><published>2009-05-29T16:21:00.000-07:00</published><updated>2009-05-29T16:38:22.407-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-29T16:38:22.407-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="release notes" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Announcing Gallio and MbUnit v3.0.6 Update 2.</title><content type="html">&lt;p&gt;Today we are releasing Gallio and MbUnit v3.0.6 Update 2.&amp;#160; This is mainly a bug fix release.&lt;/p&gt;  &lt;p&gt;Download here: &lt;a href="http://www.gallio.org/Downloads.aspx"&gt;http://www.gallio.org/Downloads.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Documentation here: &lt;a href="http://www.gallio.org/Docs.aspx"&gt;http://www.gallio.org/Docs.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Earlier release notes: &lt;a href="http://blog.bits-in-motion.com/2009/03/announcing-gallio-and-mbunit-v306.html"&gt;v3.0.6&lt;/a&gt;, &lt;a href="http://blog.bits-in-motion.com/2009/04/announcing-gallio-and-mbunit-v306.html"&gt;v3.0.6 update 1&lt;/a&gt;, &lt;a href="http://blog.bits-in-motion.com/search/label/release%20notes"&gt;all versions&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;Changes&lt;/h3&gt;  &lt;h5&gt;MbUnit:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;[New!]&lt;/strong&gt; Added &lt;em&gt;[Parallelizable(TestScope.All)] &lt;/em&gt;and &lt;em&gt;[Parallelizable(TestScope.Descendants)] &lt;/em&gt;to recursively mark tests as parallelizable such as all tests within a fixture or all fixtures in an assembly.&lt;/li&gt;    &lt;li&gt;Fixed &amp;quot;test was orphaned&amp;quot; errors involving Parallelizable tests. &lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;xUnit.net: &lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Upgraded adpater to xUnit.net v1.1 RTM.&amp;#160; &lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;NCover:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Added support for running NCover v1.5.8 as a 32-bit process on 64-bit machines. &lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Echo:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Fixed a regression that was causing test reports not to be generated when canceled. &lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;ReSharper:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Fixed ClassCastExceptions in the ReSharper runner.&lt;/li&gt;    &lt;li&gt;Fixed missing hotfix lightbulbs in the ReSharper within test files.&lt;/li&gt;    &lt;li&gt;Fixed red strikethrough that would appear in the Unit Test Session window when running MbUnit tests declared in subclassed test fixtures.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;AutoCAD:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Fixed a regression in the AutoCAD test driver.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;TeamCity:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Improved the handling of parallel tests so that TeamCity test statistics make more sense.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Miscellaneous:&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Improved the installer to support in-place upgrades.&lt;/li&gt;    &lt;li&gt;Fixed a regression that occasionally would prevent dynamic tests from running.&lt;/li&gt; &lt;/ul&gt;  &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:a01782fa-5773-433e-a526-b515aacb8056" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;,&lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/WXfU40RY688" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/1502699045126541716/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=1502699045126541716" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/1502699045126541716?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/1502699045126541716?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/05/announcing-gallio-and-mbunit-v306.html" title="Announcing Gallio and MbUnit v3.0.6 Update 2." /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;DkEHRHw-fip7ImA9WxJQFUs.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-5841217455972363181</id><published>2009-05-28T17:45:00.001-07:00</published><updated>2009-05-28T19:37:15.256-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-28T19:37:15.256-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="low-level" /><title>Registry Weirdness, UAC and the Vista Virtual Store</title><content type="html">&lt;p&gt;It appears that two different can see a different view of the registry content.&amp;#160; I seem to recall seeing mention about registry values potentially being seen differently by elevated and non-elevated applications when UAC is active.&lt;/p&gt;  &lt;p&gt;In this case, ProcessInvocation.exe sees the following value for a particular key:&amp;#160; (which is wrong!)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;C:\Source\MbUnit\v3\src\Extensions\TDNet\Gallio.TDNetRunner\bin\Gallio.TDNetRunner.dll&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;But regedit.exe sees the following value:&amp;#160; (which is correct!)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;C:\Program Files\Gallio\bin\TDNet\Gallio.TDNetRunner.dll&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;The first value was used for local development of Gallio v3.0.6 but I deleted that value!&amp;#160; In fact I deleted it a week ago and I don't think I've recreated it since (because Gallio v3.0.7 uses different keys).&lt;/p&gt;  &lt;p&gt;The second value was set by the Gallio v3.0.6 installer (uses the old key).&amp;#160; This is the value I expect to see globally.&amp;#160; All interesting applications were closed at the the time of installation.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;So why are some applications seeing the first value and other applications seeing the second value?&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;No, this is not a simple question of refreshing or shutting down applications.&amp;#160; I have shut them both down and tried again and the weird view remains persistent!&lt;/p&gt;  &lt;p&gt;Next up, reboot.&amp;#160; Argh.&lt;/p&gt;  &lt;h4&gt;After the Reboot...&lt;/h4&gt;  &lt;p&gt;Same behavior.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Why?&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Here's what I found when I searched the registry for the incorrect value:&amp;#160; (SID masked)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;HKEY_USERS\S-1-5-21-xxxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx\Software\Classes\VirtualStore\MACHINE\SOFTWARE\MutantDesign\TestDriven.NET\TestRunners\Gallio_MbUnit&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Whoa.&amp;#160; So at least I'm not dreaming.&lt;/p&gt;  &lt;h4&gt;The UAC Virtual Store&lt;/h4&gt;  &lt;p&gt;On Vista, normal users are forbidden from writing to certain folders (like &lt;em&gt;%ProgramFiles%&lt;/em&gt; and &lt;em&gt;%SystemRoot%&lt;/em&gt;) and into the &lt;em&gt;HKEY_LOCAL_MACHINE&lt;/em&gt; part of the registry.&amp;#160; Even local administrator users are forbidden from writing into these folders, &lt;em&gt;unless elevated&lt;/em&gt;.&lt;/p&gt;  &lt;p&gt;Well, not quite.&amp;#160; As a compatibility feature, instead of generating an error, a write by a non-elevated application running as a local administrator may be redirected to a Virtual Store on the file system or in the registry as appropriate.&amp;#160; The Virtual Store functions as a shallow per-user overlay of protected parts of the file system and registry.&amp;#160; Each user has a separate Virtual Store from all other users so misbehaving old-style applications can keep pretending they have write access to places they don't but the impact remains local to the user.&lt;/p&gt;  &lt;p&gt;The file system Virtual Store is in &lt;em&gt;%LOCALAPPDATA%\VirtualStore.&lt;/em&gt;&amp;#160; Here are a few things that mine contains:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;_vimrc files&lt;/li&gt;    &lt;li&gt;*.pyc files created by GNU Solfege&lt;/li&gt;    &lt;li&gt;Reflector.cfg&lt;/li&gt;    &lt;li&gt;Trillian crash dumps and user data (this explains a few oddities I've seen!)&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;The registry Virtual Store is in &lt;em&gt;HKEY_CURRENT_USER\Software\Classes\VirtualStore&lt;/em&gt;.&amp;#160; Here are a few things that mine contains:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Old obsolete registry keys I created for debugging that got me into this mess.&lt;/strong&gt;&lt;/li&gt;    &lt;li&gt;A EULA key for Acrobat Reader.&amp;#160; (Nowadays I use FoxIt Reader.)&lt;/li&gt;    &lt;li&gt;Configuration for a few audio codecs.&lt;/li&gt;    &lt;li&gt;An MRU list for DirectDraw (weird).&lt;/li&gt;    &lt;li&gt;A flag set by my 3G wireless software.&lt;/li&gt;    &lt;li&gt;Configuration for OpenVPN.&lt;/li&gt; &lt;/ul&gt;  &lt;h4&gt;What Actually Happened&lt;/h4&gt;  &lt;p&gt;Once upon a time, I ran a little script &lt;em&gt;without elevation&lt;/em&gt; to create some registry keys for local development.&amp;#160; Since the script was not elevated, the new registry keys were created within the &lt;em&gt;Virtual Store&lt;/em&gt;.&lt;/p&gt;  &lt;p&gt;Later on, I ran a script to delete these keys.&amp;#160; But in the interim I had changed the script to run with elevation.&amp;#160; Because it ran &lt;em&gt;with elevation&lt;/em&gt; it did not see the keys in the &lt;em&gt;Virtual Store &lt;/em&gt;so it left them there.&lt;/p&gt;  &lt;p&gt;Then I installed Gallio v3.0.6 which, being an installer, created some new registry keys &lt;em&gt;with elevation&lt;/em&gt; in the expected location.&lt;/p&gt;  &lt;p&gt;When I tried to run a test, the ProcessInvocation.exe component of TestDriven.Net enumerated these keys in the registry, but &lt;em&gt;without elevation&lt;/em&gt;.&amp;#160; Consequently it saw the old keys in the &lt;em&gt;Virtual Store&lt;/em&gt; and got extremely confused.&lt;/p&gt;  &lt;p&gt;Crash!&lt;/p&gt;  &lt;h4&gt;Moral&lt;/h4&gt;  &lt;p&gt;Normally the Virtual Store on Vista is not a problem.&amp;#160; Actually it's a pretty clever solution to a serious compatibility problem.&amp;#160; However, if you ever run into issues like mine you might just need to learn about it so that you can delete or repair the magic virtualized content if needed.&lt;/p&gt;  &lt;p&gt;More info: &lt;a href="http://support.microsoft.com/kb/927387"&gt;http://support.microsoft.com/kb/927387&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;   &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:17cce9e1-8eb2-4399-93a5-c5545cfb59bd" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/mIAOkdv4jVo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/5841217455972363181/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=5841217455972363181" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5841217455972363181?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5841217455972363181?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/05/vista-registry-weirdness.html" title="Registry Weirdness, UAC and the Vista Virtual Store" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;C0cNRnw7fip7ImA9WxJQFU0.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-7515860118149049448</id><published>2009-05-28T01:57:00.001-07:00</published><updated>2009-05-28T01:58:17.206-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-28T01:58:17.206-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><title>Vista UAC and Dog Food</title><content type="html">&lt;p&gt;A few days ago I re-enabled Vista UAC on my development laptop.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Why?&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;I was developing a few features for Gallio that required being able to perform privilege elevations on Vista.&amp;#160; So unless I got my act together to play nice with UAC, those features were just not going to work!&lt;/p&gt;  &lt;p&gt;&lt;em&gt;With reluctance I re-enabled UAC.&amp;#160; Sometimes dog food, while nutritious, contains unpleasant additives.&lt;/em&gt;&lt;/p&gt;  &lt;h4&gt;Teaser&lt;/h4&gt;  &lt;p&gt;Here's one of the new features from Gallio v3.0.7 in trunk:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/Sh5R6AJtMjI/AAAAAAAAAZA/dkAwqDjRq40/s1600-h/image%5B3%5D.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="473" alt="image" src="http://lh3.ggpht.com/_X_hvY6E0egY/Sh5R65Y1mLI/AAAAAAAAAZE/jjIwB2HpOIA/image_thumb%5B1%5D.png?imgmax=800" width="512" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;When certain settings are changed, we show shield buttons:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_X_hvY6E0egY/Sh5R7d5JoPI/AAAAAAAAAZI/Tlc__dg5Bd0/s1600-h/image%5B7%5D.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="33" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/Sh5R73ZKp4I/AAAAAAAAAZM/4ARUs9JzkvA/image_thumb%5B3%5D.png?imgmax=800" width="247" border="0" /&gt;&lt;/a&gt;&amp;#160; &lt;/p&gt;  &lt;p&gt;And of course when clicking on one of those buttons we'll get the standard UAC elevation dialog.&amp;#160; I can't show you that because by design UAC blocks out other applications including SnagIt.&lt;/p&gt;  &lt;h4&gt;How it works (very briefly)&lt;/h4&gt;  &lt;p&gt;To display shield buttons, set the Button's FlatStyle to System and then send the BCM_SETSHIELD message to the button using SendMessage.&amp;#160; (Or borrow my &lt;a href="http://code.google.com/p/mb-unit/source/browse/trunk/v3/src/Gallio/Gallio.UI/Controls/ShieldButton.cs"&gt;ShieldButton&lt;/a&gt; class.&amp;#160; Ignore the Mono bits.)&lt;/p&gt;  &lt;p&gt;Before performing privilege elevation, check whether it is even needed for the current user.&amp;#160; Here's one way.&amp;#160; A better way might be to examine the Win32 ProcessToken directly.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;public static bool CurrentUserHasElevatedPrivileges()     &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; return principal.IsInRole(WindowsBuiltInRole.Administrator);      &lt;br /&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;To perform privilege elevation, first split the code into two parts: non-privileged and privileged.&amp;#160; The privileged part will have to run in a separate process one way or another.&amp;#160; Then decide how to perform the elevation using one of these options.&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Use the COM elevation moniker as in the &lt;a href="http://msdn.microsoft.com/en-us/library/ms679687(VS.85).aspx"&gt;CoCreateInstanceAsAdmin&lt;/a&gt; example to instantiate a COM object within an elevated process provided by the system.&amp;#160; This method is convenient but it requires that you have registered your privileged code as a COMVisible managed class ahead of time.&amp;#160; Consequently, we do not use this method in Gallio which is expected to be xcopy deployable with no prior installation of components required (besides copying files).      &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;Create a separate executable process that contains the privileged code.&amp;#160; Be sure to embed an appropriate &lt;a href="http://msdn.microsoft.com/en-us/library/aa374191.aspx"&gt;application manifest&lt;/a&gt; like the following either using the Visual Studio manifest build       &lt;p&gt;option or &lt;a href="http://msdn.microsoft.com/en-us/library/aa375649.aspx"&gt;mt.exe&lt;/a&gt;.        &lt;br /&gt;        &lt;br /&gt;Our incantation of mt.exe looks a bit like this: &amp;quot;mt.exe -nologo -manifest Elevated.manifest -managedassemblyname:Gallio.Host.Elevated.exe -nodependency -outputresource:Gallio.Host.Elevated.exe;#1&amp;quot;.&amp;#160; Keep in mind that if you use mt.exe on a signed assembly that the signature will be invalidated and the assembly will need to be re-signed with &amp;quot;sn.exe -R ...&amp;quot;.&amp;#160; You might consider delay-signing the assembly at first, then merging the manifest, then re-signing the assembly.&amp;#160; This way you can tell whether you're working with an assembly with a proper manifest because the original delay-signed copy will fail validation (normally) but the patched and re-signed copy will be good.        &lt;br /&gt;        &lt;br /&gt;Here is the Elevated.manifest file that we use for Gallio.Host.Elevated.exe:&lt;/p&gt;   &lt;/li&gt;    &lt;p&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;yes&amp;quot;?&amp;gt;     &lt;br /&gt;&amp;#160; &amp;lt;assembly xmlns=&amp;quot;urn:schemas-microsoft-com:asm.v1&amp;quot; manifestVersion=&amp;quot;1.0&amp;quot;&amp;gt;      &lt;br /&gt;&amp;#160; &amp;lt;description&amp;gt;Provides out of process hosting for Gallio components that require privilege elevation.&amp;lt;/description&amp;gt;      &lt;br /&gt;&amp;#160; &amp;lt;trustInfo xmlns=&amp;quot;urn:schemas-microsoft-com:asm.v2&amp;quot;&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;security&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;requestedPrivileges xmlns=&amp;quot;urn:schemas-microsoft-com:asm.v3&amp;quot;&amp;gt;      &lt;br /&gt;&lt;strong&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;requestedExecutionLevel level=&amp;quot;requireAdministrator&amp;quot; uiAccess=&amp;quot;false&amp;quot;/&amp;gt;       &lt;br /&gt;&lt;/strong&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/requestedPrivileges&amp;gt;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/security&amp;gt;      &lt;br /&gt;&amp;#160; &amp;lt;/trustInfo&amp;gt;      &lt;br /&gt;&amp;lt;/assembly&amp;gt;&lt;/p&gt;    &lt;p&gt;Launch the privileged process directly being sure to set a few extra properties:&lt;/p&gt;    &lt;p&gt;var processStartInfo = new ProcessStartInfo(&amp;quot;PrivilegedApp.exe&amp;quot;);     &lt;br /&gt;processStartInfo.Verb = &amp;quot;runas&amp;quot;;&amp;#160;&amp;#160; // optional if you embedded a manifest      &lt;br /&gt;processStartInfo.UseShellExecute = true;&amp;#160; // mandatory      &lt;br /&gt;processStartInfo.ErrorDialog = true;&amp;#160; // mandatory      &lt;br /&gt;processStartInfo.ErrorDialogOwnerHandle = ownerForm.Handle;&amp;#160; // recommended&lt;/p&gt; &lt;/ol&gt;  &lt;p&gt;Ok, I just glossed over a million little details...&lt;/p&gt;  &lt;p&gt;If you are ever in a position to have to perform privilege elevation in a managed application, I highly recommend reading the relevant MSDN articles three times over.&lt;/p&gt;  &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:b57311b2-1918-4106-9e19-506ec13597c5" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/Yt7DG60heLc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/7515860118149049448/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=7515860118149049448" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/7515860118149049448?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/7515860118149049448?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/05/vista-uac-and-dog-food.html" title="Vista UAC and Dog Food" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_X_hvY6E0egY/Sh5R65Y1mLI/AAAAAAAAAZE/jjIwB2HpOIA/s72-c/image_thumb%5B1%5D.png?imgmax=800" height="72" width="72" /><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DUcHSXs4fSp7ImA9WxJRF0Q.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-663875646858312623</id><published>2009-05-19T22:23:00.001-07:00</published><updated>2009-05-19T22:23:58.535-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-19T22:23:58.535-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Gallio on Visual Studio 2010, Redux</title><content type="html">&lt;p&gt;After the Microsoft PDC of 2008, I released a variant of Gallio v3.0.5 with support for the Visual Studio 2010 pre-release bits.&lt;/p&gt;  &lt;p&gt;So today I have been working on getting Gallio v3.0.7 running on Visual Studio 2010 Beta 1.&lt;/p&gt;  &lt;p&gt;The migration has been pretty easy for the most part.&amp;#160; With the PDC bits, I had a hard time producing a build because I had to install all sorts of tools inside the PDC VM and deal with rather unstable bits.&amp;#160; With the Beta 1 release, the process is much smoother since I can just install the framework on the build server.&amp;#160; Visual Studio 2010 Beta 1 is working ok although I have filed a couple of bugs already.&lt;/p&gt;  &lt;p&gt;Unfortunately since not all contributors to Gallio will be running VS 2010 anytime soon I will have to maintain a parallel set of project files and solutions.&amp;#160; I guess I will be maintaining *.vs2008.csproj and *.vs2010.csproj files side-by-side for a little while.&amp;#160;&amp;#160; I wish Microsoft would invest some effort in providing a smooth backward-compatible upgrade path for projects to enable developers on newer tools to work with projects created using older tools without having to actually migrate them over.&amp;#160; (This is my 3rd Visual Studio upgrade and I'm getting really tired of this story.)&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;The first pre-release installer should be available later this week.&lt;/p&gt;  &lt;p&gt;Looks promising so far...&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/ShOT4RtGWjI/AAAAAAAAAY4/Rd02N2r_nBc/s1600-h/image%5B3%5D.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="514" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/ShOT7MmwdkI/AAAAAAAAAY8/1gGipR3ihaY/image_thumb%5B1%5D.png?imgmax=800" width="644" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:36fd3eda-21a3-4e8b-9cec-be530114d939" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;,&lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/d3dBhPu_k-k" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/663875646858312623/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=663875646858312623" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/663875646858312623?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/663875646858312623?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/05/gallio-on-visual-studio-2010-redux.html" title="Gallio on Visual Studio 2010, Redux" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/_X_hvY6E0egY/ShOT7MmwdkI/AAAAAAAAAY8/1gGipR3ihaY/s72-c/image_thumb%5B1%5D.png?imgmax=800" height="72" width="72" /><thr:total>5</thr:total></entry><entry gd:etag="W/&quot;D08FRXYzfCp7ImA9WxJXEk8.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-5928896221209394034</id><published>2009-05-08T23:47:00.001-07:00</published><updated>2009-06-05T11:16:54.884-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-05T11:16:54.884-07:00</app:edited><title>NDepend v2.12 Rocks.</title><content type="html">&lt;p&gt;I have used NDepend on and off for the past couple of years.&amp;#160; It has never been an everyday thing, but whenever I have an itch to find out just how convoluted my code dependency graph has become, NDepend has been there for me.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Full disclosure: Patrick Smacchia sent me a free professional license of NDepend to play with some time ago.&amp;#160; To be honest, I didn't really need the license because I was already using the OSS edition of NDepend at the time.&amp;#160; I just think this is a cool tool.&amp;#160; :-)&lt;/em&gt;&lt;/p&gt;  &lt;h4&gt;Download and Installation&lt;/h4&gt;  &lt;p&gt;Installing NDepend is pretty easy.&amp;#160; Just visit the &lt;a href="http://www.ndepend.com/NDependDownload.aspx"&gt;NDepend download page&lt;/a&gt;, type in your email address for the OSS or trial version or your license code for the progressional addition, then download the NDepend ZIP file.&amp;#160; Extract the ZIP someplace useful (like C:\Program Files) then pin a shortcut to VisualNDepend.exe to your Quick Launch bar.&lt;/p&gt;  &lt;h5&gt;Tools Integration&lt;/h5&gt;  &lt;p&gt;Now launch VisualNDepend.&amp;#160; To enable Visual Studio and Reflector integration click on the button in the UI.&amp;#160; A couple more clicks and you're done.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SgUmjPt-47I/AAAAAAAAAXQ/1NJZtmRygMs/s1600-h/image7.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="95" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SgUmj6Y5HqI/AAAAAAAAAXU/fgvOondNyr0/image_thumb3.png?imgmax=800" width="380" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SgUmkWQRrLI/AAAAAAAAAXY/m9HjlV_qPmg/s1600-h/image11.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="250" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/SgUmlSg5i6I/AAAAAAAAAXc/e4caFaU4maY/image_thumb5.png?imgmax=800" width="334" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I have to say, unboxing NDepend is very slick!&amp;#160; There are a bunch of links to little screencast demos to explain how stuff works.&amp;#160; I have to admit I have tool envy here.&amp;#160; I wish my tools could help the user along like this.&lt;/p&gt;  &lt;p&gt;That said, I do wish NDepend (and Reflector also) had an MSI installer flavor.&amp;#160; It's harder to explain to others how to extract ZIPs and create shortcuts than to just ask them to run through a standard installer process.&lt;/p&gt;  &lt;h4&gt;Dissecting Gallio with NDepend&lt;/h4&gt;  &lt;p&gt;After installing the NDepend add-in for Visual Studio, I opened up the Gallio solution.&amp;#160; There's a lot of stuff in here, so for this exploration I'm going to start by looking at the dependencies within &lt;strong&gt;Gallio.dll&lt;/strong&gt;.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SgUmmL4S8XI/AAAAAAAAAXg/w7yhivBZQIs/s1600-h/image15.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="104" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SgUmm97w4dI/AAAAAAAAAXk/lwiUN3WDGYY/image_thumb7.png?imgmax=800" width="523" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;It's been a few months since I looked at this so I'm cringing here...&amp;#160; What will I discover?&lt;/p&gt;  &lt;p&gt;Up pops an instance of Visual NDepend with this dialog listing all of the assemblies in my solution.&amp;#160; Pretty slick.&amp;#160; I don't have to create a new NDepend project from scratch.&amp;#160; On the other hand, I'm only interested in Gallio.dll and I did right-click specifically on that project in Visual Studio so I will remove everything else.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_X_hvY6E0egY/SgUmpBGX7RI/AAAAAAAAAXo/NN38JB0ZQy0/s1600-h/image19.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="406" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SgUmqFx048I/AAAAAAAAAXs/nX06DbiO2lc/image_thumb9.png?imgmax=800" width="716" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h5&gt;NDepend Report&lt;/h5&gt;  &lt;p&gt;The next thing that pops up is an NDepend report.&amp;#160; It shows up in my default web browser (FireFox).&amp;#160; I think it might be cooler if it appeared inside of the Visual NDepend application itself using an embedded web browser (eg. System.Windows.Forms.WebBrowser) but this report is pretty useful anyways.&lt;/p&gt;  &lt;p&gt;Bunch of metrics regarding both the source code and the compiled binaries.&amp;#160; Looks like Gallio.dll consists 66% of comments.&amp;#160; Heh.&amp;#160; There's all of the XML documentation!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_X_hvY6E0egY/SgUmrIMsGQI/AAAAAAAAAXw/fhpeNTdAlUM/s1600-h/image24.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="77" alt="image" src="http://lh3.ggpht.com/_X_hvY6E0egY/SgUmsBbtC_I/AAAAAAAAAX0/U6Ck4Z_Rxvc/image_thumb12.png?imgmax=800" width="644" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;One thing to keep in mind when reading the report is that not all of the information is equally useful.&amp;#160; NDepend computes lots of different metrics to help you find hot spots.&amp;#160; Of course, if there's nothing really wrong then what it finds might not be very interesting.&amp;#160; You need to read the report intelligently.&lt;/p&gt;  &lt;h5&gt;Top 10 Methods to Refactor?&lt;/h5&gt;  &lt;p&gt;For example, here are the top 10 methods in Gallio.dll that I should refactor, according to NDepend.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/SgUmtbsIk7I/AAAAAAAAAX4/RxaZb9FnL2Y/s1600-h/image28.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="269" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SgUmuxskfAI/AAAAAAAAAX8/0PF_Uzsgdok/image_thumb14.png?imgmax=800" width="767" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Whoa, so the top 2 are compiler generated and most of the others are pretty simple methods.&amp;#160; Based on the criteria used (see CQL below).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_X_hvY6E0egY/SgUmwP4oe1I/AAAAAAAAAYA/iQDgVoi3z-Q/s1600-h/image32.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="232" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/SgUmxQ5NzNI/AAAAAAAAAYE/_zbhVWAao5M/image_thumb16.png?imgmax=800" width="798" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The main thing I'm hitting up against is the IL nesting depth and number of variables.&amp;#160; None of these methods have a particularly troubling cyclomatic complexity except for CreateFilteredClosure which has a CC of 20 but can't really be improved much more given what it does.&lt;/p&gt;  &lt;p&gt;So in sum, I guess I'm doing ok!&amp;#160; Cool.&amp;#160; However I bet if I threw this at some production code of my employer, we'd find some interesting things to talk about.&lt;/p&gt;  &lt;p&gt;Let's keep digging.&lt;/p&gt;  &lt;h5&gt;Finding Large Methods&lt;/h5&gt;  &lt;p&gt;Out of the box, the NDepend report shows a summary of large methods.&amp;#160; This is worth paying close attention to.&lt;/p&gt;  &lt;p&gt;Here are the 10 biggest methods in Gallio.dll.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SgUmzanN3eI/AAAAAAAAAYI/MfrMrnM8LYk/s1600-h/image41.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="318" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SgUm0gfJGAI/AAAAAAAAAYM/4nND9zb2q1o/image_thumb21.png?imgmax=800" width="1034" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I'm amused that FindMiddleSnake made the list.&amp;#160; That is one monster method!&amp;#160; Unfortunately I can't really do much to improve it because of the nature of the &lt;a href="http://citeseer.ist.psu.edu/myers86ond.html"&gt;algorithm&lt;/a&gt;.&amp;#160; Calculating diffs is hard stuff!&amp;#160; The topological sort is in a similar category.&lt;/p&gt;  &lt;p&gt;However, the PluginCatalog.ApplyTo and DefaultRuntime.VerifyInstallation methods are long for no good reason.&amp;#160; So with a little help from &lt;a href="http://www.jetbrains.com/resharper/"&gt;ReSharper&lt;/a&gt;'s &lt;em&gt;Extract Method&lt;/em&gt; refactoring here's what ApplyTo now looks like:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_X_hvY6E0egY/SgUm14tJ99I/AAAAAAAAAYQ/Hs6w9b7U11Q/s1600-h/image45.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="199" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SgUm25jhI1I/AAAAAAAAAYU/EOmsdnbmig8/image_thumb23.png?imgmax=800" width="571" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h5&gt;Overlap with FxCop&lt;/h5&gt;  &lt;p&gt;Some of the metrics that NDepend computes out of the box produce similar results to those that could be obtained using &lt;a href="http://en.wikipedia.org/wiki/FxCop"&gt;FxCop&lt;/a&gt;.&amp;#160; This is rather interesting since NDepend's CQL language actually has the potential to be much more expressive and configurable than the FxCop verifications, if you like.&amp;#160; However there can be some quirks.&lt;/p&gt;  &lt;p&gt;Here are some methods and constructors that appear in abstract classes that were marked &amp;quot;public&amp;quot; but that NDepend thinks could be &amp;quot;protected&amp;quot;.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_X_hvY6E0egY/SgUm4CM-6AI/AAAAAAAAAYY/DxokOA36IG4/s1600-h/image49.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="212" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/SgUm5ELudeI/AAAAAAAAAYc/SeHR4euCTw0/image_thumb25.png?imgmax=800" width="736" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The &lt;em&gt;NativeCodeElementWrapper&lt;/em&gt;, &lt;em&gt;NativeMemberWrapper&lt;/em&gt; and &lt;em&gt;NativeFunctionWrapper&lt;/em&gt; constructors I understand (fixed!), the others not so much.&amp;#160; &lt;em&gt;TestTypePatternAttribute&lt;/em&gt; is a public attribute class whose constructor really does need to be public because it is public API although NDepend doesn't know that.&amp;#160; The &lt;em&gt;NativeCodeElementWrapper.Target &lt;/em&gt;property is only used internally in Gallio.dll so it could be made protected or internal if desired.&lt;/p&gt;  &lt;p&gt;What it comes down to is that you still have to use your head when you interpret the data.&amp;#160; It's worth the effort though!&lt;/p&gt;  &lt;h4&gt;Finding Inadvertent Coupling&lt;/h4&gt;  &lt;p&gt;Of all of the many metrics and views provided by NDepend, this one is by far my favorite.&amp;#160; It shows the afferent coupling (incoming dependencies) in blue, efferent coupling (outgoing dependencies) in green, and mutual coupling (both incoming and outgoing dependencies) in black.&amp;#160; The following chart is displaying coupling among namespaces.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/SgUm57u2XuI/AAAAAAAAAYg/VVFnbF8_t2A/s1600-h/image53.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="303" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/SgUm6rZU_4I/AAAAAAAAAYk/ith-tHQOVAg/image_thumb27.png?imgmax=800" width="487" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Oh no!&amp;#160; There's some black in there.&amp;#160; The black means that some code in one namespace is using code in another namespace which also happens to use code in the former. &lt;/p&gt;  &lt;p&gt;Namespaces are frequently used to separate subsystems from one another.&amp;#160; Coupling among namespaces can indicate one of the following problems:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;One or more classes are in the wrong namespace and should be moved. &lt;/li&gt;    &lt;li&gt;One or more classes have dependencies on the wrong things and should be refactored to use dependency injection or use inversion of control to access foreign services. &lt;/li&gt;    &lt;li&gt;The namespaces are not very cohesive and the subsystems they represent should be restructured to be more tightly encapsulated or have fewer responsibilities. &lt;/li&gt;    &lt;li&gt;There are common services partially defined in both namespaces that should be extracted and moved elsewhere. &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;To take a deeper look I just double-click on one of the black squares to drill down.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_X_hvY6E0egY/SgUm8Lgg5SI/AAAAAAAAAYo/9jykM43QqGA/s1600-h/image61.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="514" alt="image" src="http://lh5.ggpht.com/_X_hvY6E0egY/SgUm885wYRI/AAAAAAAAAYs/9zod1ryWbwA/image_thumb31.png?imgmax=800" width="527" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Ah ha!&amp;#160; It looks like the &lt;em&gt;Gallio.Model.Diagnostics.ExceptionData&lt;/em&gt; class is being used by several of the classes in the &lt;em&gt;Gallio.Runtime.Logging &lt;/em&gt;namespace&lt;em&gt;.&lt;/em&gt;&amp;#160; This is bad.&lt;/p&gt;  &lt;p&gt;The &lt;em&gt;Runtime&lt;/em&gt; subsystem is the lowest tier of Gallio and provides the core foundational services needed to get Gallio up and running.&amp;#160;&amp;#160; The &lt;em&gt;Model&lt;/em&gt; subsystem is one of the tiers above the &lt;em&gt;Runtime&lt;/em&gt; and its job is to define the test object model.&amp;#160; What we have here is a cyclic dependency that crosses tiers.&lt;/p&gt;  &lt;p&gt;In a tiered architecture, dependencies should always flow from one tier down to the ones below it and never the other way around.&amp;#160; If you let dependencies flow both ways then you may quickly find yourself enmeshed in a bowl of spaghetti!&lt;/p&gt;  &lt;p&gt;It looks like in order to fix this problem, I'm going to have to rearrange a few of these dependencies.&amp;#160; The upside is that the structure of the system will be cleaner when I am done.&lt;/p&gt;  &lt;h4&gt;CQL: A Recipe for Analysis&lt;/h4&gt;  &lt;p&gt;NDepend provides a code query language called CQL which enables all sorts of fancy analysis.&amp;#160; In addition to answering various questions interactively, you can incorporate NDepend CQL constraints into your build process such that the build will fail if a constraint is violated.&amp;#160; By using CQL as part of a continuous integration process you can validate system design conventions early and often.&amp;#160; For example, you can add a CQL constraint to detect when cyclic dependencies are accidentally created across tiers.&lt;/p&gt;  &lt;p&gt;To be honest, I have not delved into CQL very much myself.&amp;#160; Using it as part of the build process looks like it could be a very useful tool for managing large teams of developers.&amp;#160; (I just need to find more time to do architecture work...)&lt;/p&gt;  &lt;h4&gt;Summary&lt;/h4&gt;  &lt;p&gt;NDepend is a good tool that repays careful analysis.&amp;#160; It won't fix your system design defects for you but it will help you find the trouble spots.&lt;/p&gt;  &lt;p&gt;As a result of writing this post, I fixed a few big methods and performed a major refactoring of Gallio's primary namespaces guided mainly by NDepend's dependency matrix view.&amp;#160; This is not the first time I have used NDepend to improve Gallio.&amp;#160; Here is how that dependency matrix is looking now:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_X_hvY6E0egY/SgUm933-1EI/AAAAAAAAAYw/-YTMRYXIPp0/s1600-h/image%5B3%5D.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="228" alt="image" src="http://lh4.ggpht.com/_X_hvY6E0egY/SgUm_FUWKiI/AAAAAAAAAY0/3_7W3acgDLc/image_thumb%5B1%5D.png?imgmax=800" width="417" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;All in all, I feel much better now!&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/Mliu9Yns4hE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/5928896221209394034/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=5928896221209394034" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5928896221209394034?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/5928896221209394034?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/05/ndepend-v212-rocks.html" title="NDepend v2.12 Rocks." /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_X_hvY6E0egY/SgUmj6Y5HqI/AAAAAAAAAXU/fgvOondNyr0/s72-c/image_thumb3.png?imgmax=800" height="72" width="72" /><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;CUUDQnc4eSp7ImA9WxJTEks.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-2749542810510903070</id><published>2009-04-20T14:33:00.001-07:00</published><updated>2009-04-20T14:34:33.931-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-20T14:34:33.931-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software engineering" /><category scheme="http://www.blogger.com/atom/ns#" term="values" /><title>Underfoot</title><content type="html">&lt;p&gt;Every now and then we talk about the &amp;quot;overhead&amp;quot; associated with implementing better practices. &lt;/p&gt;  &lt;p&gt;For example, designing a stratified architecture requires some up-front planning to separate the tiers, identify roles, decide on the interfaces, establish conventions, and resolve cross-cutting concerns.&amp;#160; During implementation, there will almost certainly be additional code required to marshal messages across tiers, additional configuration required to glue them together and additional infrastructure required to host them.&amp;#160;&amp;#160; Quite probably the team will also need to learn new skills and adopt other ancillary design/implementation practices at the same time to make it all work out.&amp;#160; This effort all takes time, effort, and money. &lt;/p&gt;  &lt;p&gt;Likewise adopting TDD/BDD/DbE, using continuous integration, leveraging IoC, adopting one-way asynchronous message passing (instead of RPC), applying command-query separation, and &amp;quot;going SOLID&amp;quot; all have one-time and recurring costs... (and benefits of course.)&lt;/p&gt;  &lt;p&gt;I think &amp;quot;overhead&amp;quot; is a misleading label for these costs.&amp;#160; It presumes that the costs are excess terms that could be eliminated through greater efficiency.&amp;#160; However these costs in fact purchase greater efficiency.&amp;#160; They are an investment in laying a foundation for future.&amp;#160; They pad the space &lt;em&gt;under our feet&lt;/em&gt; not the space &lt;em&gt;over our heads&lt;/em&gt;.&amp;#160; They let us &lt;em&gt;reach greater heights&lt;/em&gt;!&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Let's try this paragraph with&lt;em&gt; underfoot&lt;/em&gt; instead of &lt;em&gt;overhead.&lt;/em&gt;&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Building this proposed multi-tier platform will have an initial &lt;em&gt;20% underfoot&lt;/em&gt; while we retool and lay the foundation.&amp;#160; Once that is done we will be &lt;em&gt;50%&lt;/em&gt; more efficient though we will have to spend an additional recurring &lt;em&gt;5% underfoot&lt;/em&gt; to keep the axles greased. &lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Which do you prefer?&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/Gnw0SDvdzSw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/2749542810510903070/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=2749542810510903070" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/2749542810510903070?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/2749542810510903070?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/04/underfoot.html" title="Underfoot" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>5</thr:total></entry><entry gd:etag="W/&quot;A04BQX47fip7ImA9WxJQFk4.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-8193803244451151122</id><published>2009-04-02T02:00:00.000-07:00</published><updated>2009-05-29T16:32:30.006-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-29T16:32:30.006-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="release notes" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Announcing Gallio and MbUnit v3.0.6 Update 1.</title><content type="html">&lt;p&gt;Today we are releasing Gallio and MbUnit v3.0.6 Update 1.&amp;#160; This release fixes a regression in the ReSharper support and adds a couple of new features. &lt;/p&gt;  &lt;p&gt;&lt;em&gt;Highlights:&lt;/em&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;em&gt;Added support for TestDriven.Net category filters.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;&lt;em&gt;Added support for more powerful inclusion/exclusion test filter expressions.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;&lt;em&gt;Fixed ReSharper v3.1, v4.0 and v4.1 hangs.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;&lt;em&gt;Increased NCover v1.5.8 timeout.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;&lt;em&gt;Adopted new assembly version numbering scheme.&lt;/em&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Download here: &lt;a href="http://www.gallio.org/Downloads.aspx"&gt;http://www.gallio.org/Downloads.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Documentation here: &lt;a href="http://www.gallio.org/Docs.aspx"&gt;http://www.gallio.org/Docs.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Earlier release notes: &lt;a href="http://blog.bits-in-motion.com/2009/03/announcing-gallio-and-mbunit-v306.html"&gt;v3.0.6&lt;/a&gt;, &lt;a href="http://blog.bits-in-motion.com/search/label/release%20notes"&gt;all versions&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;Changes&lt;/h3&gt;  &lt;h4&gt;Inclusion / Exclusion Filters&lt;/h4&gt;  &lt;p&gt;Gallio provides a mechanism for filtering the set of tests to run.&amp;#160; This is typically used to select some subset, for example to run all tests &lt;strong&gt;except&lt;/strong&gt; for integration tests.&lt;/p&gt;  &lt;p&gt;However, the filter grammar used in previous versions could not correctly express an exclusion constraint.&amp;#160; Several people noted that filters like &lt;em&gt;&amp;quot;not Category: Integration&amp;quot;&lt;/em&gt; did not in fact work due to a logical error introduced by a change in how Gallio applies filters.&amp;#160; Oops.&lt;/p&gt;  &lt;p&gt;In this update we have added a more concrete concept of inclusion and exclusion filters by extending the filter syntax with &lt;strong&gt;include&lt;/strong&gt; and &lt;strong&gt;exclude&lt;/strong&gt; clauses.&lt;/p&gt;  &lt;p&gt;Here are a few examples using Gallio.Echo.&amp;#160; The same filter syntax is used universally throughout Gallio so you can use them with NAnt, MSBuild, and PowerShell as well.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Gallio.Echo.exe MyTestAssembly.dll &amp;quot;/f:&lt;strong&gt;exclude Category: Integration&lt;/strong&gt;&amp;quot;&lt;/li&gt;    &lt;li&gt;Gallio.Echo.exe MyTestAssembly.dll &amp;quot;/f:&lt;strong&gt;include Type: MyFixture&lt;/strong&gt;&amp;quot;      &lt;br /&gt;&lt;em&gt;(here the &amp;quot;include&amp;quot; is optional)&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;Gallio.Echo.exe MyTestAssembly.dll &amp;quot;/f:&lt;strong&gt;AuthorName: Jeff and Category: SmokeTests exclude Type: BrokenFixture, AnotherBrokenFixture exclude Importance: NoOneReallyCaresAbout&lt;/strong&gt;&amp;quot;&lt;/li&gt; &lt;/ul&gt;  &lt;h4&gt;TestDriven.Net Category Filters&lt;/h4&gt;  &lt;p&gt;As of v2.17, TestDriven.Net has a Visual Studio preferences page that allows the user to configure a couple of different settings.&lt;/p&gt;  &lt;p&gt;In this update of Gallio we have added support for the TestDriven.Net category filter options.&amp;#160; This feature was absent in previous releases because we did not then have a way to express exclusion filters properly.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_X_hvY6E0egY/SdSAbMBM7gI/AAAAAAAAAXE/9dIwA3Exytw/s1600-h/image%5B3%5D.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="371" alt="image" src="http://lh6.ggpht.com/_X_hvY6E0egY/SdSAcEvJ1jI/AAAAAAAAAXI/RnZpL2LoSJw/image_thumb%5B1%5D.png?imgmax=800" width="644" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;New Assembly Version Numbering Scheme&lt;/h4&gt;  &lt;p&gt;Plug-in authors rejoice!&amp;#160; I've changed how assembly version numbers are constructed in a subtle but important way.&lt;/p&gt;  &lt;p&gt;Previously the assembly version number would change after each build.&amp;#160; As a result, code linked to &lt;em&gt;Gallio&lt;/em&gt; &lt;em&gt;v3.0.6.&lt;strong&gt;749&lt;/strong&gt;&lt;/em&gt; wouldn't work with &lt;em&gt;Gallio&lt;/em&gt; &lt;em&gt;v3.0.6.&lt;strong&gt;762&lt;/strong&gt;&lt;/em&gt; despite there being no significant API changes between those versions.&lt;/p&gt;  &lt;p&gt;Now the last component of the assembly version number is always &lt;strong&gt;&lt;em&gt;0&lt;/em&gt;&lt;/strong&gt;.&amp;#160; This way there can be no assembly version conflicts within a given release branch.&amp;#160; However, we still need a way to distinguish different builds.&amp;#160; So the file version number contains the full version number.&lt;/p&gt;  &lt;p&gt;For example, in this release the version numbers are set as follows:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Assembly version: &lt;em&gt;v3.0.6.&lt;strong&gt;0&lt;/strong&gt;.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;File version: &lt;em&gt;v3.0.6.&lt;strong&gt;763&lt;/strong&gt;&lt;/em&gt;.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;As a result, you should no longer need to recompile your plug-ins except to make them work with a new release branch.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Aside: There is now a separate branch for Gallio and MbUnit v3.0.6 in case you want to check out the code and play with it.&amp;#160; This will also help us keep maintenance updates on track.&lt;/em&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/qImog7w7308" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/8193803244451151122/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=8193803244451151122" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/8193803244451151122?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/8193803244451151122?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/04/announcing-gallio-and-mbunit-v306.html" title="Announcing Gallio and MbUnit v3.0.6 Update 1." /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_X_hvY6E0egY/SdSAcEvJ1jI/AAAAAAAAAXI/RnZpL2LoSJw/s72-c/image_thumb%5B1%5D.png?imgmax=800" height="72" width="72" /><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;DUQHR344eSp7ImA9WxVbFk4.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-9048307995117079348</id><published>2009-04-01T18:55:00.001-07:00</published><updated>2009-04-01T18:55:36.031-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-01T18:55:36.031-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>Gallio is not Alpha Anymore (And Has Not Been Since Last Year)</title><content type="html">&lt;p&gt;I sometimes receive questions from people about when Gallio and MbUnit v3 will be out of alpha.&lt;/p&gt;  &lt;p&gt;The answer is that they have been out of alpha officially since the v3.0.4 release last year.&amp;#160; The current releases should be considered stable and final.&amp;#160; Sure, there are still bugs to be fixed and enhancements yet to be made but we do consider these releases quite acceptable for use in production systems.&lt;/p&gt;  &lt;p&gt;So there you go.&amp;#160; :-)&lt;/p&gt;  &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:63b9a87b-e268-4d9c-9d2c-8eab8e18521f" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;,&lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/4iHnSsBewpY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/9048307995117079348/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=9048307995117079348" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/9048307995117079348?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/9048307995117079348?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/04/gallio-is-not-alpha-anymore-and-has-not.html" title="Gallio is not Alpha Anymore (And Has Not Been Since Last Year)" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DkMDQXs4cCp7ImA9WxVbFkw.&quot;"><id>tag:blogger.com,1999:blog-9023944205100803414.post-1996824163806403776</id><published>2009-04-01T12:30:00.001-07:00</published><updated>2009-04-01T12:34:30.538-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-01T12:34:30.538-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gallio" /><category scheme="http://www.blogger.com/atom/ns#" term="mbunit" /><title>How to enable or disable Visual Studio Team Test integration</title><content type="html">&lt;p&gt;Gallio provides integration with Visual Studio Team Test so that you can run your tests using the built-in Visual Studio Test View and other tools.&lt;/p&gt;  &lt;p&gt;However to make this work, the project file must be configured with an extra &amp;quot;ProjectTypeGuid&amp;quot; that that informs Visual Studio that the project should be considered a Test Project.&amp;#160; By adding or removing this element you can enable or disable support for Visual Studio Team Test.&lt;/p&gt;  &lt;h4&gt;Caveats&lt;/h4&gt;  &lt;ul&gt;   &lt;li&gt;We do not install the Visual Studio Team Test integration by default anymore beginning with v3.0.6 if you perform a &amp;quot;Typical&amp;quot; install.&amp;#160; You will need to perform a &amp;quot;Complete&amp;quot; installation or ensure that the &amp;quot;Visual Studio Team System Test Runner&amp;quot; component under &amp;quot;Test Runners&amp;quot; is installed as part of a &amp;quot;Custom&amp;quot; installation.&lt;/li&gt;    &lt;li&gt;The built-in MbUnit project templates already contain the appropriate ProjectTypeGuids, so if you create a new project that way then it should just work out of the box.&lt;/li&gt;    &lt;li&gt;Some editions of Visual Studio lack support for Test Projects and will &lt;em&gt;refuse&lt;/em&gt; to open up any project that contains the special guid!&amp;#160; In this case you may have to remove the offending guid so that you can work with your projects.&lt;/li&gt;    &lt;li&gt;The Visual Studio Team Test integration is very rough.&amp;#160; It has poor performance due to several issues including the fact that Visual Studio wants to populate its list of tests in the foreground after compiling a test project.&amp;#160; So the GUI may hang for some time after each build if the Test View is open and you have a big test project.&amp;#160; I really hope they fix this in Visual Studio 2010.&lt;/li&gt;    &lt;li&gt;At this time, the integration only works with MbUnit v2, MbUnit v3 and xUnit.net.&amp;#160; Unfotunately csUnit and NUnit do not support the use of an abstract reflection policy so they are not supported by this feature.&amp;#160; However, there does exist another Visual Studio Team Test add-in for NUnit.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;In all honesty, I would recommend using TestDriven.Net or ReSharper to run your tests inside Visual Studio instead of using the Test View.&lt;/p&gt;  &lt;h4&gt;Editing Project Files&lt;/h4&gt;  &lt;p&gt;Visual Studio uses a magic &amp;quot;ProjectTypeGuid&amp;quot; to determine whether it should consider a given project to be a Test Project.&amp;#160; Only Test Projects are considered when populating the Test View.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;And the magic guid is... {3AC096D0-A1C2-E12C-1390-A8335801FDAB}&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Here's how to edit the files:&lt;/p&gt;  &lt;p&gt;1. Open up the *.csproj or *.vbproj file in a text editor.&amp;#160; It's just XML (actually an MSBuild build script).&lt;/p&gt;  &lt;p&gt;2. Within the &lt;strong&gt;first&lt;/strong&gt; &amp;lt;PropertyGroup&amp;gt; element at the top of the file, look for an existing &amp;lt;ProjectTypeGuids&amp;gt; element.&lt;/p&gt;  &lt;p&gt;2a. If you found the &amp;lt;ProjectTypeGuids&amp;gt; element, then add the aforementioned Guid to the list within if not already present.&amp;#160; Be sure to separate the guids with semicolons.&lt;/p&gt;  &lt;p&gt;2b. If you did not find a &amp;lt;ProjectTypeGuids&amp;gt; element, then copy and paste in the appropriate element below.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;p&gt;&lt;strong&gt;C# Projects: *.csproj         &lt;br /&gt;          &lt;br /&gt;&lt;/strong&gt;&amp;lt;ProjectTypeGuids&amp;gt;{3AC096D0-A1C2-E12C-1390-A8335801FDAB};        &lt;br /&gt;{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&amp;lt;/ProjectTypeGuids&amp;gt;&lt;/p&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;p&gt;&lt;strong&gt;Visual Basic Projects: *.vbproj         &lt;br /&gt;          &lt;br /&gt;&lt;/strong&gt;&amp;lt;ProjectTypeGuids&amp;gt;{3AC096D0-A1C2-E12C-1390-A8335801FDAB};        &lt;br /&gt;{F184B08F-C81C-45F6-A57F-5ABD9991F28F}&amp;lt;/ProjectTypeGuids&amp;gt;&lt;/p&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;3. Save the project file.&lt;/p&gt;  &lt;p&gt;4. Reload the project in Visual Studio.&lt;/p&gt;  &lt;p&gt;5. Open up the Test View.&amp;#160; The tests in your project should now be populated.&lt;/p&gt;  &lt;h4&gt;It's Too Slow... How Do I Turn It Off?&lt;/h4&gt;  &lt;p&gt;If you read the caveats above, I warned you... :-)&lt;/p&gt;  &lt;p&gt;There are three ways to turn this off.&amp;#160; Any one of them will suffice.&lt;/p&gt;  &lt;p&gt;1. Remove the ProjectTypeGuid just added above then reload the project.&lt;/p&gt;  &lt;p&gt;2. Disable the Gallio add-in using the Visual Studio Add-In Manager.&amp;#160; Contrary to what some people expect, this will not in any way affect the ReSharper integration.&lt;/p&gt;  &lt;p&gt;3. Uninstall the &amp;quot;Visual Studio Team System Test Runner&amp;quot; component.&amp;#160; You don't need to uninstall all of Gallio for this.&amp;#160; Use the Add/Remove programs control panel to Change the set of installed features and look under the &amp;quot;Test Runners&amp;quot; part of the tree.&lt;/p&gt;  &lt;h4&gt;What The Future Holds...&lt;/h4&gt;  &lt;p&gt;I do have a hack in mind for improving performance by populating the Visual Studio test list asynchronously although it would run contrary to the stated API contracts.&amp;#160; So if the performance is a problem for you but you really really want to use the Visual Studio Team Test controls, please let me know and I'll raise the priority of this task.&amp;#160; Also I could use a volunteer to help make the integration more compelling overall.&lt;/p&gt;  &lt;p&gt;Also, some time ago I started working on a different native Visual Studio add-in for Gallio called Sail.&amp;#160; It's not ready yet but something I've been thinking about again lately...&amp;#160; If you'd like to help, let me know.&lt;/p&gt;  &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:e747cb72-528d-4696-b8c7-2654c803434d" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/gallio" rel="tag"&gt;gallio&lt;/a&gt;,&lt;a href="http://technorati.com/tags/mbunit" rel="tag"&gt;mbunit&lt;/a&gt;&lt;/div&gt;  &lt;img src="http://feeds.feedburner.com/~r/BitsInMotion/~4/NKE5eHMZDxo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.bits-in-motion.com/feeds/1996824163806403776/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=9023944205100803414&amp;postID=1996824163806403776" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/1996824163806403776?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9023944205100803414/posts/default/1996824163806403776?v=2" /><link rel="alternate" type="text/html" href="http://blog.bits-in-motion.com/2009/04/how-to-enable-or-disable-visual-studio.html" title="How to enable or disable Visual Studio Team Test integration" /><author><name>Jeff Brown</name><uri>http://www.blogger.com/profile/09075745057339916352</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://lh3.google.com/image/jeff.brown/RffvJOb_Z3I/AAAAAAAAAAk/3xAr3Z7z2OE/s144/me.jpg" /></author><thr:total>0</thr:total></entry></feed>
