Test driving Conway's Game of Life in Ruby : Part 2 of 2

Contd... Part 2
Do the evolution (with due apologies to Pearl Jam)
Let’s model our simulation with a file aptly titled ‘GameOfLife.rb’

Rule#1 : Any live cell with fewer than two live neighbours dies, as if by loneliness.
Red
Create new test case class

ts_GameOfLife.rb
require 'test/unit'
require 'Colony'
require 'GameOfLife'

class Test_GameOfLife < Test::Unit::TestCase

def test_Evolve_Loneliness
@obColony = Colony.readFromFile("Colony1.txt");
@obColony = GameOfLife.evolve(@obColony)
assert( !@obColony.isCellAlive(1, 0) )
assert( @obColony.isCellAlive(1, 1) )
assert( !@obColony.isCellAlive(2, 2) )
end
end


Since we want to run all the tests from multiple files now, we can create a “test suite” It is as easy as creating a new file named ts_GameOfLife.rb.
Naming conventions : Prefix ts_ for test suites..
ts_GameOfLife.rb
require 'test/unit'
require 'tc_Colony'
require 'tc_GameOfLife'


We can then run the test suite with
ruby ts_GameOfLife.rb

Green
We traverse each cell in the colony and kill the cell if it is alive
class GameOfLife

def evolve ( obColony )
(0..maxRows).each{ |iRow|
(0..maxCols).each{ |iCol|
killCellIfLonely(obColony, iRow, iCol)
}
}
return obColony;
end

private
def killCellIfLonely(obColony, iRow, iCol)
if (obColony.isCellAlive(iRow, iCol) && ( obColony.getLiveNeighbourCount( iRow, iCol ) < 2 ) )
obColony.markCell( iRow, iCol, false )
end
end

end


Detour
Hmm but we don’t have getLiveNeighbourCount() and markCell(). Let’s test drive
Small red

tc_Colony.rb
def test_GetLiveNeighbourCount()
assert_equal( 2, @obColony.getLiveNeighbourCount(0,0) )
assert_equal( 0, @obColony.getLiveNeighbourCount(0,3) )
assert_equal( 1, @obColony.getLiveNeighbourCount(1,3) )
assert_equal( 0, @obColony.getLiveNeighbourCount(2,3) )
end


Small green
Colony.rb
def getLiveNeighbourCount( iRow, iCol )
iCountOfLiveNeighbours = 0

obCoordinatesToExamine = [
[iRow-1, iCol-1], [iRow-1, iCol], [iRow-1, iCol+1],
[iRow, iCol-1], [iRow, iCol+1],
[iRow+1, iCol-1], [iRow+1, iCol ], [iRow+1, iCol+1] ]

obCoordinatesToExamine.each{ |curRow, curCol|
iCountOfLiveNeighbours = iCountOfLiveNeighbours+1 if (isCellInBounds(curRow, curCol) && isCellAlive(curRow, curCol))
}
return iCountOfLiveNeighbours
end

def isCellInBounds( iRow, iCol)
return ((0...@maxRows) === iRow) &amp;& ((0…@maxCols) === iCol)
end


Small Red
def test_MarkCell()
assert( !@obColony.isCellAlive(0,0) )

@obColony.markCell( 0, 0, true )
assert( @obColony.isCellAlive(0,0), "Should have born now!" )

@obColony.markCell( 0, 0, false )
assert( !@obColony.isCellAlive(0,0), "Should have died now!" )
end


Small Green
def markCell( iRow, iCol, bIsCellAlive )
@colonyGrid[iRow][iCol] = ( bIsCellAlive ? "1" : "0" )
return
end


Refactor
I see some duplication with “1” and “0” – but I’ll refactor with Strike 3.

Hmmm still Red is my evolve test case. Had to use a couple of traces to find the bugger .. I left out a dot in the bold section below. 2 dots – inclusive range, 3 dots – max bound is not part of the range.

def evolve ( obColony )
obNewColony = obColony.clone()

(0...obColony.maxRows).each{ |iRow|
(0...obColony.maxCols).each{ |iCol|
#print "Count = " + obColonySnapshot.getLiveNeighboursCount( iRow, iCol ).to_s
if ( obColony.getLiveNeighboursCount( iRow, iCol ) < 2 )
obNewColony.markCell( iRow, iCol, false )
end
}
}

return obNewColony;
end

Colony.rb
def clone()
#return Colony.new( @maxRows, @maxCols, Array.new(@colonyGrid) )
return Colony.new( @maxRows, @maxCols, @colonyGrid.map{ |elem| elem.clone } )
end


Refactor

Well I got bit with the ranges. I used 0..obColony.maxRows instead of 0…obColony.maxRows and it blew up my test. I think traversing the Colony should be handled / provided by the colony itself. I don’t want anyone making the same mistake.

Let’s move that into a ruby iterator style Colony.each() method. So here it is
Colony.rb
def each
(0...@maxRows).each{ |iRow|
(0...@maxCols).each{ |iCol|
yield iRow, iCol
}
}
end


so now I can use this in GameOfLife#evolve, Colony#to_s and Test_Colony#test_IsCellAlive. Code is even smaller!!

def evolve ( obColony )
obNewColony = obColony.clone()

obColony.each{ |iRow, iCol|
if ( obColony.getLiveNeighboursCount( iRow, iCol ) < 2 )
obNewColony.markCell( iRow, iCol, false )
end
}

return obNewColony;
end

Colony.rb
def to_s
sGridListing = ""
each{ |iRow, iCol|
sGridListing += ( isCellAlive( iRow, iCol) ? "@ " : ". " )
sGridListing += "\n" if iCol.succ == @maxCols
}
return sGridListing
end


Next we need a test for Colony#clone that we sneakily added in to fix this test. We need to check if the cloned colony is similar to the original ..
tc_Colony.rb
def test_Clone
clonedColony = @obColony.clone()
assert_not_equal( clonedColony.object_id, @obColony.object_id, "Cloned object is the same as original" )

@obColony.each{ |iRow, iCol|
assert_equal( @obColony.isCellAlive(iRow, iCol),
clonedColony.isCellAlive(iRow, iCol),
"Contents differ at " + iRow.to_s + ", " + iCol.to_s )
}
end


Ok let’s take a look at our next rule.
Rule#2 - Any live cell with more than three live neighbours dies, as if by overcrowding.
Let’s create a test colony for this rule.

Colony_Overcrowding.txt
3, 4
0 0 1 0
1 1 1 1
0 0 1 1


Red
tc_GameOfLife.rb
def test_Evolve_Overcrowding
arrExpected = [false, false, true, false,
false, false, false, false,
false, false, false, true]

@obColony = Colony.readFromFile("Colony_Overcrowding.txt");
@obColony = GameOfLife.new.evolve(@obColony)

iLooper = 0
@obColony.each{ |iRow, iCol|
assert_equal( arrExpected[iLooper], @obColony.isCellAlive(iRow, iCol),
"Mismatch at " + iRow.to_s + ", " + iCol.to_s )
iLooper += 1
}
end


Green
Ok so we kill em off even if there are too many of them in an area ! Just another OR clause …
def evolve ( obColony )
obNewColony = obColony.clone()

obColony.each{ |iRow, iCol|
if obColony.isCellAlive(iRow, iCol) and
( (obColony.getLiveNeighboursCount(iRow,iCol) < 2) or (obColony.getLiveNeighboursCount(iRow,iCol) > 3) )
obNewColony.markCell( iRow, iCol, false )
end
}
return obNewColony;
end



Rule#3 - Any dead cell with exactly three live neighbours comes to life.
Red

Colony_Birth.txt
5, 5
0 0 0 0 1
1 1 0 0 1
0 0 1 0 0
0 0 1 0 0
0 0 1 0 0

tc_GameOfLife.rb
def test_Evolve_Birth
arrExpected = [false, false, false, false, false,
false, true, false, true, false,
false, false, true, true, false,
false, true, true, true, false,
false, false,false, false, false
]

@obColony = Colony.readFromFile("Colony_Birth.txt");
@obColony = GameOfLife.new.evolve(@obColony)

iLooper = 0
@obColony.each{ |iRow, iCol|
assert_equal( arrExpected[iLooper], @obColony.isCellAlive(iRow, iCol),
"Mismatch at " + iRow.to_s + ", " + iCol.to_s )
iLooper += 1
}
end


Green
GameOfLife.rb
def evolve ( obColony )
obNewColony = obColony.clone()

obColony.each{ |iRow, iCol|
if obColony.isCellAlive(iRow, iCol)
if ( (obColony.getLiveNeighboursCount(iRow,iCol) < 2) or (obColony.getLiveNeighboursCount(iRow,iCol) > 3) ) then
obNewColony.markCell( iRow, iCol, false )
end
else
obNewColony.markCell( iRow, iCol, true ) if ( obColony.getLiveNeighboursCount(iRow,iCol) == 3 )
end
}
return obNewColony;
end


Hmm… that broke my overcrowding test. Oh yeah, as per the new rule, there is a newborn in that colony. Let me just update the expected results as

arrExpected = [false, false, true, true,
false, false, false, false,
false, false, false, true]

Refactor
The tests are duplicating code to traverse and check the Colony with the expected results/array. Extract method
private
def assert_colonies_are_similar( arrExpectedValues, obColony )
iLooper = 0
obColony.each{ |iRow, iCol|
assert_equal( arrExpectedValues[iLooper], obColony.isCellAlive(iRow, iCol),
"Mismatch at " + iRow.to_s + ", " + iCol.to_s )
iLooper += 1
}
end


refactored test looks like :
def test_Evolve_Overcrowding
arrExpected = [false, false, true, true,
false, false, false, false,
false, false, false, true]

obColony = Colony.readFromFile("Colony_Overcrowding.txt");

assert_colonies_are_similar( arrExpected, GameOfLife.new.evolve(obColony) )
end


And that’s a wrap !!
Finally I quickly write up what I learn is called a “Driver”
ARGV is an array that contains the command line parameters. I’ll take two.. thank you!
One for the input colony text file and the second for the number of generations.
We then just evolve and keep printing the colony.

if (ARGV.length != 2) then
puts "Usage : ruby GameOfLife.rb [ColonyTextFile] [No of generations]"
exit
end
if (!File.exist?(ARGV[0])) then
puts "Specified Colony File does not exist"
exit
end


obColony = Colony.readFromFile(ARGV[0]);

0.upto(ARGV[1].to_i) { |iGenNo|
puts obColony.to_s + "Gen:" + iGenNo.to_s

obColony = GameOfLife.new.evolve(obColony)
}

Run it as
L:\Gishu\Ruby\GameOfLife>ruby GameOfLife.rb TestColony.txt 100

You can also redirect output to a file
L:\Gishu\Ruby\GameOfLife>ruby GameOfLife.rb TestColony.txt 100 > a.txt


Open it up in Notepad for example… resize the height such that you can see one line below the Gen : X line. Now use the PgUp or PgDown keys to see evolution in action!!

By the way the TestColony.txt file contains the colony called Gosper’s glider gun. Watch the output and read the wiki to know why ! 

Hey this program is slow for 100+ generations.. I am at chapter 2 and find that there is another better way to implement this… a different perspective on this problem that cuts down the grid traversal for better performance.

Till next time….
Gishu
Feb 18, 2006

[Update : ]
A special Thanks to http://blog.wolfman.com/articles/2006/05/26/howto-format-ruby-code-for-blogs
for a good tip and an even cooler ruby script on pretty-formatting ruby code for html display.

Also the ruby-185-21 windows package seems to be broken. the gem install fails with a
"getaddrinfo: no address associated with hostname". I went back to 1.8.2 to get this done today !

Test driving Conway's Game of Life in Ruby : Part I

Well I had Ruby on my to-learn list for some time now. The pickaxe (don't leave your ruby-home without it) was getting rusty in a corner. I also had another book, Data Structures and Program Design in C, something I had picked up for the interview of my first job. So I decided why not translate the examples in the latter in Ruby – two birds with one stone ??

One that I liked up front (Chapter 1) is Conway’s Game of Life. The rules are explained here along with some cool information. This is basically a “cellular automaton”.. sounds ultra nerdy doesn’t it? Don’t worry sounds as greek to me as it does to you. In simple terms, it is a simulation of a colony of cells evolving. There are some rules governing the birth and mortality of cells. See the wikipedia page for details

Anyways, the writeup is also present in the uploaded zip -
http://tech.groups.yahoo.com/group/testdrivendevelopment/files/
"TDD_Conways_GameOfLife_Ruby.zip"


Let’s begin.
First I need to figure out how to write a test in Ruby. Pick up the pickaxe… What the…. now it can’t be that easy… can it?
Let’s create a file called GameOfLife.rb. Important items in bold below

require "test/unit"
class Test_GameOfLife < Test::Unit::TestCase

def test_GetOne
assert_equal(1, GameOfLife.new.getOne);
end
end


class GameOfLife
end


L:\Gishu\Ruby\GameOfLife>ruby GameOfLife.rb
Loaded suite GameOfLife
Started
E
Finished in 0.016 seconds.

1) Error:
test_GetOne(Test_GameOfLife):
NoMethodError: undefined method `getOne' for #<GameOfLife:0x2ba14d8>
GameOfLife.rb:5:in `test_GetOne
'

1 tests, 0 assertions, 0 failures, 1 errors



Ok we have a code red !! Let’s get it green… quick to the test mobile..
require "test/unit"
class Test_GameOfLife < Test::Unit::TestCase

def test_GetOne
assert_equal(1, GameOfLife.new.getOne);
end
end


class GameOfLife
end


L:\Gishu\Ruby\GameOfLife>ruby GameOfLife.rb
Loaded suite GameOfLife
Started
E
Finished in 0.016 seconds.

1) Error:
test_GetOne(Test_GameOfLife):
NoMethodError: undefined method `getOne' for #<GameOfLife:0x2ba14d8>
GameOfLife.rb:5:in `test_GetOne
'

1 tests, 0 assertions, 0 failures, 1 errors
require "test/unit"
class Test_GameOfLife < Test::Unit::TestCase

def test_GetOne
assert_equal(1, GameOfLife.new.getOne);
end
end

class GameOfLife
def getOne
return 1
end
end


L:\Gishu\Ruby\GameOfLife>ruby GameOfLife.rb
Loaded suite GameOfLife
Started
.
Finished in 0.0 seconds.

1 tests, 1 assertions, 0 failures, 0 errors


Woo hoo !!! I have my first green light in Ruby. I feel the confidence running through my veins now… I had heard about Ruby’s principle of least surprise.. I’m fascinated. Beam me up, Scotty !!

The advent of colonization
Let’s make the input to the program a file. This file would contain the current snapshot of a “colony” of cells. The first line in the file shall tell us the max rows and columns in the colony
Colony1.txt
3, 4


So we need a colony class that can tell us the bounds of a colony in an input file.
Colony.ReadFromFile - ReadBounds
Naming conventions : Test case classes are saved in files prefixed with tc_

tc_Colony.rb

require 'test/unit'
require 'Colony'

class Test_Colony < Test::Unit::TestCase

def test_ReadFromFile
c = Colony.readFromFile("Colony1.txt");
assert_equal(3, c.maxRows);
assert_equal(4, c.maxCols);
end
end

Colony.rb
class Colony
def Colony.readFromFile( sColonyFile )
end
end


Green
Initialize is a method that would be called whenever a Colony.new is issued. We take in 2 parameters for maxRows and maxCols to know the bounds.

Colony.readFromFile: We also have our first taste of File I/O. We read the first line and use regular expressions to break the line into 2 parts. We convert them to integers via to_i() and use them to create a Colony instance.
class Colony
attr_reader :maxRows, :maxCols

def initialize( iRows, iCols )
@maxRows=iRows
@maxCols=iCols
end

def Colony.readFromFile( sColonyFile )
sColonyRows = sColonyCols = ""
File.open(sColonyFile) { |obFile|
sDimensions = obFile.gets
sColonyRows, sColonyCols = sDimensions.scan(/\d+/)
}
return Colony.new(sColonyRows.to_i, sColonyCols.to_i)
end
end


Refactor
Inline the variable sDimensions.
def Colony.readFromFile( sColonyFile )
sColonyRows = sColonyCols = ""
File.open(sColonyFile) { |obFile|
sColonyRows, sColonyCols = obFile.gets.scan(/\d+/)
}
return Colony.new(sColonyRows.to_i, sColonyCols.to_i)
end


Colony.IsCellAlive
Since I am new to this.. I wrote the “read from file” test in 2 parts. Part 2 – now we read the actual colony. Update the Colony1.txt file as follows
Colony1.txt
3, 4
0 0 0 0
1 1 0 0
0 0 0 1


Red
tc_Colony.rb
def test_IsCellAlive
c = Colony.readFromFile("Colony1.txt");
expected = [
false, false, false, false,
true, true, false, false,
false, false, false, true ]

iExpectedLooper = 0;
(0...c.maxRows).each{ |iRow|
(0...c.maxCols).each{ |iCol|
assert_equal( expected[iExpectedLooper], c.isCellAlive( iRow, iCol ) )
iExpectedLooper = iExpectedLooper.succ
}
}
end

Colony.rb
def isCellAlive( iRow, iCol )
return false;
end


Now to show that I’m not making all this up…
L:\Gishu\Ruby\GameOfLife>ruby tc_Colony.rb
Loaded suite tc_Colony
Started
F.
Finished in 0.094 seconds.

1) Failure:
test_IsCellAlive(Test_Colony)
[tc_Colony.rb:23:in `test_IsCellAlive'
tc_Colony.rb:21:in `each
'
tc_Colony.rb:21:in `test_IsCellAlive'
tc_Colony.rb:20:in `each
'
tc_Colony.rb:20:in `test_IsCellAlive']:
<true> expected but was
<false>.

2 tests, 7 assertions, 1 failures, 0 errors


Green
String.scan can parse a line and return an array. So this should work for us…
sRow.scan(/\d/) - > [“0”, “0”, “0”, 0”]
So I build up a 2D Array like this and stash it away in the Colony object.
[ [“0”, “0”, “0”, 0”], [“1”, “1”, “0”, “0”], [“0”, “0”, ”0”, “1” ] ]

class Colony
attr_reader :maxRows, :maxCols

def initialize( iRows, iCols, obColony2DArray )
@maxRows=iRows
@maxCols=iCols
@colonyGrid=obColony2DArray
end

def Colony.readFromFile( sColonyFile )
sColonyRows = sColonyCols = ""
ob2DArray ||= []

File.open(sColonyFile) { |obFile|
sColonyRows, sColonyCols = obFile.gets.scan(/\d+/)

while(sRow = obFile.gets)
ob2DArray.insert(-1, sRow.scan(/\d/) )
end
}
return Colony.new(sColonyRows.to_i, sColonyCols.to_i, ob2DArray)
end

def isCellAlive( iRow, iCol )
return ( @colonyGrid[iRow][iCol] == "1" ? true : false )
end
end


Refactor
For the ones who came in late, I can factor out common code into a setup method, which is guaranteed to be executed before every test. So off with duplication !!

Add a member variable @obColony and initialize it inside setup()
tc_Colony.rb

def setup
@obColony = Colony.readFromFile("Colony1.txt");
end


to_s() – ToString() for the .Net ters
Red
def test_ToString
assert_equal( ". . . . \n@ @ . . \n. . . @ \n",
@obColony.to_s )
end

1) Failure:
test_ToString(Test_Colony) [tc_Colony.rb:34]:
<". . . . \n@ @ . . \n. . . @ \n"> expected
but was
<"#<Colony:0x2b9c9a8>">.


Green
def to_s
sGridListing = ""
(0…maxRows).each{ |iRow|
(0...maxCols).each{ |iCol|
sGridListing += ( isCellAlive( iRow, iCol) ? "@ " : ". " )
}
sGridListing += "\n"
}
return sGridListing
end

On to Part 2

Test Driving a GUI [WinForms application]

Nearly half a year to write this thing between my day-job and my inertia.
I just took a example project in C# with XML for the data and tried to TDD it on my own. I kept an account of all the decisions, mistakes and backtracks that I had to make along the way.

In the end I have
a 1000+ line code base with 170 odd automated tests and 98% coverage
200+ page manuscript that grew bit by bit
a much smarter me

You'll find TDD_GUI_DotNet-Gishu.zip at
http://tech.groups.yahoo.com/group/TestFirstUserInterfaces/files/

One of the key take-aways for me, was never do a TOY example. Do something that's of some use as a product for you or atleast someone else. I kinda lost interest towards the end and rushed up the end.. I just wanted to switch to my next item on the list.

It's Ruby-time now !!!

Eclipse 3.2 vs Netbeans 5.5

How times change.... I'm now into Java land evaluating IDEs... Seems like yesterday when it looked like I would be doing .NET and C# winforms till I pass out!

  • Configuring offline javadoc API help:
Just get the offline docs zip at http://java.sun.com/docs/index.html
Then it was a matter of configuring the IDEs to bring them up at a keypress.
In Netbeans it was just a matter of unzipping them to a docs folder under your jdk directory. Press Alt + F1 and you're on your way.
In Eclipse it was a whole lot of work, you have to unzip the archive as above. Then open up project properties (Project Menu > Properties). Change the javadoc setting to use the offline doc folder path instead of the http:/// online help setting . See the attached image to see where it is hidden ... definitely not meant for idiots like me. Then press Shift+F2 to bring up in an external browser or F1 to bring it up in an IDE view.
+ 1 to Netbeans !!!!




ASP.NET 2.0 membership provider not working with SQL 2005

I faced a peculiar problem while deploying my Web application.
I developed the application using Express editions. This tied me to SQL Express behind the scenes with some magic default connection strings. The machine for deployment had SQL 2005 installed and no SQL Express. I used the security controls to set up the login-to-website flow quick n easy and now they barfed like there's no tomorrow on the target machine.

To cut a long story short, there are some mods required to get the default SQL membership provider to work with SQL 2005. Here's the dope on that...

http://weblogs.asp.net/bsimser/archive/2005/11/20/431029.aspx
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/ConfigASPNET_SQL.asp


Thanks both you guys... this was driving me nuts.

Die! Die! Excel, you Fiend !

I was recently drafted to write an ASP Site that documents on the server to create a "status" page. Given the affinity towards Word and Excel for people in QA, it was no surprise I was soon wrestling with Excel to read some stuff from an xls file.

I got the site running with some major hiccups like not finding type definitions for objects returned by some functions. In the end I ended up using Reflection to get at the custom properties in the excel document.

Now to my horror, I found that the task manager showing zillions of Excel instances quietly ganging up on my web server. So I asked around and I was amazed at the reputation Excel had carved up for itself. I first suspected COM References... I have a negative bias towards unmanaged code of any nature.
First stop: Guidelines on how to release Word or Excel if it is not shutting down gracefully
http://support.microsoft.com/kb/317109/

Still no luck! Next I was lucky to have my logic in a separate DLL that I was test driving via NUnit. This allowed me to make the interesting observation that a test run did not leave Excel instances running. That means my code is good. It has something to do with that ASP.NET 2.0 web page. I did a quick scan and found I had hardly any code in there to cause this. More googling and finally it dawned on me

I will post the gems I found after a week of manic activity

Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when run in this environment.

WARNING: Office was not designed, and is not safe, for unattended execution on a server. Developers who use Office in this manner do so at their own risk.

http://support.microsoft.com/kb/257757/
http://support.microsoft.com/kb/288368/

The answer seems to be that Office applications were not built for scalabilty or running in a multi-threaded environment. So until Office 2007 hold on to your horses !!!

I ended up not creating multiple instances and settling for an instance that is stashed away as a static variable, which seems to slowly eating up memory. But this is an internal web site, so I don't mind.... I think ASP.NET will restart the app if the memory consumed goes above a certain limit.

Integrating NCover code coverage with your CCNet CIS

I managed to read one more chapter in the CIS story. This one is called ‘Integrating NCover with CruiseControl’.
The CruiseControl documentation page has most of the help you need.
CruiseControl.NET/Doc/CCNET/Using%20CruiseControl.NET%20with%20NCover.html
But NCover has always been a tricky one. If that page worked, I wouldn’t be writing this piece here.

Ingredients:


  1. A CruiseControl.Net (I use v1.0.1) build server running all green. And you better be TDDing that code too…J.

  2. NAnt I use this (v 0.85) to build my code.

  3. NCover I use version 1.3.3 since that’s the only one there that still works with VS2003. Pick up 1.5.x for VS 2005.


Once you have all that in place, it’s time to get this started. To run NCover we need to add a task to our build file. It differs from the one on the CCNet doc page in the placement of the quotation marks. I found my solution by watching the Nant Build log on the CCNet dashboard

<exec program="l:\tools\NCover\ncover.console.exe" commandline="/w AT_Bin\Debug /c &quot;L:\Program Files\NUnit_227\bin\nunit-console.exe&quot; TestBearings.dll" />


Save that one. Now move onto the ccnet.config file. Add this to the tasks block of your project element there. We need this so that code coverage report is merged to the Nant build output file.

<merge>
<files>
<file>L:\BuildFolder\AT_Bin\debug\Coverage.Xml</file>
</files>
</merge>


That’s it. Save and start a build. Hopefully that’s a green. Click on the green build link on the Recent Builds tab. Click on NCover report. You should be seeing something like this.


Now to find out why I don’t have 100% coverage J
Just that you know it is possible, If you have a different stylesheet for NCover results, rename and replace the stylesheet named

CruiseControl.NET\server\xsl\NCover.xsl
with your own. Hit F5 to refresh your browser and you should see the new results. Here I have use the default xsl that comes with NCover – although the CCNet version is more to the point. The one I mentioned in my earlier post is the best. I got an unexpected console listing at the top … but no big deal.


How to make your .NET application disappear from the Task Switch (Alt + Tab) list

I kinda stumbled on this via a bug-report.
Basically if you have your WinForm (e.g. a modal dialog) properties
• BorderStyle set to FixedToolWindow or SizableToolWindow
• AND ShowInTaskBar set to false

When the form is up and you hit Alt+Tab, the task switch won't have your application in the list. You live you learn...

Task: Write an automated test for a detected deadlock

Here is a simplified version of the problem case

public void PingLicense()
{
lock(this)
{
CheckAuthorization();
}
}

public void GetLicense( out License obCurLicense)
{
lock(this)
{
//talk to non-threadsafe 3rd party lib to query the license info
}
}

CheckAuth() function does a GetLicense and raises events to notify others if a change in License occurs.
Ping License is called by a helper thread via a Timer object (every minute )

So here is how the deadlock occurred, if I remove the license h/w device midway. On the next PingLicense call, helper thread acquires the lock on the object and raises the event for everyone to switch to Unauth mode.
One of the subscribers is the UI ( to enable/disable UI elements ). *Any changes to the UI in .NET must be done on the thread in which the UI was created*
So the event handler requests a switch to the UI (Main) Thread. In the event handler, there is a call to GetLicense(). DeadLock !!!
UI thread is stuck requesting a lock for the object (which PingLicense currently holds). Ping License won't relinquish the lock since the event handler hasn't returned.

Agreed that it seems dumb now. But Hindsight is always...

Now the fix is - PingLicense doesn't need a lock block ( a result of a mindless strafing run inserting lock blocks ) . Removing that solved it.
But no code change without a failing test... Also it kinda bugged me that this got thru my AT Test store.



My Attempt

After some discussion with the nice folks on the TDD list, here's my AT (took 10 mins with Console Output verification)

[Test]
public void Test_CR9672_PollingThread_ShouldNotAcquireLock_BlocksEventHandlers_OnGetLicense()
{
LicTestHelper.WriteLicenseConfig(LicTestHelper.LocalHL_LIC_INFO);
LicenseManager.Instance.EvApplicationAuthorized += new EventHandler(On_LicMgr_EvApplicationAuthorized_CR9672);

DynamicMock obMockHL = SetupDynamicMockHLWrapper();
//... set up a mock to mock out the license hardware

GObjectSpy.CallPrivateMethod( LicenseManager.Instance, ReflectionStrings.S_LICMGR_PINGLICENSE_METH, new object[]{null} );
//Code will block here if a deadlock happens.
// If test completes, the test has passed
obMockHL.Verify();

}

private void On_LicMgr_EvApplicationAuthorized_CR9672(object sender, EventArgs e)
{
Thread obAnotherThread = new Thread( new ThreadStart( this.DummyHandler ) );
obAnotherThread.Start();
obAnotherThread.Join();
}

private void DummyHandler()
{
License obCurLicense;
// Query License which blocks indefinitely if PingLicense is holding a lock
LicenseManager.Instance.GetLicense(out obCurLicense);
}



I added a temporary log via Console.WriteLine to prove the test is authentic
# Test Failure log - test hangs
Lock Request by Thread 353 (PingLicense)
GotLock!
First Thread 353
Event Handler Thread 268
Lock Request by Thread 268 (GetLicense)

# Test Success log
Lock Request by Thread 359 (PingLicense) // did not remove Console.WriteLine - actually no lock request
GotLock! // did not remove Console.WriteLine - actually no lock request
First Thread 359
Event Handler Thread 261
Lock Request by Thread 261 (GetLicense)
GotLock!
EventHandler exit
PingLicense exit

Task completed!