by Mikael Henriksson
23. June 2010 21:52
I had a delicate issue on the new server. Builds would work for any given period of time and then I got a message about java jada jada jada time out. As it turns out there is a bug with the jgit library that TeamCity uses and unfortunately Jetbrains does not have rights or something to modify this by themselves since it’s a third party library. The strange thing is that it was never a problem on my machine but on the server it turned out to cause problems.
A quick search actually brought me straight to the solution. The information is contained within a YouTRACK issue http://youtrack.jetbrains.net/issue/TW-9574 but I’ll summarize it here in brevity.
- Inside the .BuildServer/config directory create a new file called internal.properties
- Copy the contents from my internal.properties at the bottom
- Download jetbrains.git.zip
- Stop TeamCity
- Delete the .BuildServer\plugins\jetbrains.git folder
- Delete the .BuildServer\plugins\jetbrains.git.zip file
- Copy the new jetbrains.git.zip to .BuildServer\plugins\
- Start TeamCity
teamcity.git.fetch.separate.process=true
teamcity.git.fetch.timeout=0
teamcity.git.clone.timeout=0
That’s it, TeamCity should start checking for changes again. If not I recommend contacting Jetbrains about it.
-Have fun
by Mikael Henriksson
23. June 2010 20:12
Last post I said I would come back with information about how to move TeamCity from one computer to another. This part of TeamCity I really like. Let’s establish some ground rules first. For this to work at all both computers/servers need to run the same version of TeamCity. Second if you like me had everything running fine locally backed by a SQL Server the server also needs this for things to move smoothly.
So that’s the gibberish now let’s move on to the solution.
- Take a full backup of TeamCity on the original machine
- Move the backup zip file to the TeamCity copy
- From the .BuildServer/Config (aka <TeamCity Data Directory>) grab the database.properties file and copy this file to where ever you want on the new machine and modify it if necessary to point to the new database. Don’t forget the problems with the port and the instance name
- Fire up a remote session to the new machine and start a command prompt
- Stop the TeamCity services
- Run the maintainDB command at the bottom of this post
- Copy the database.properties file to the .BuildServer/config dir
- Start TeamCity services. It should now fire up with all the build configs etc from the old machine.
cd "C:\Program Files (x86)\TeamCity\bin\"
maintainDB.cmd restore -F "C:\TeamCity_Backup_20100619_150814.zip" -T "C:\database.properties"
-F – specifies the path to the backup archive
-T – specifies specifies the path to the database.properties.
The only thing left now is to run all the builds and watch the lights go green and smile :)
by Mikael Henriksson
19. June 2010 16:42
The first time I installed TeamCity it took me about a day to get everything in place (excluding setting up the build configurations). Second time I installed TeamCity on a real server it took me about 30 minutes including setting up all the build configurations. I didn’t really do any second time, I transferred the database.properties file and a backup to the server.
There are a few tricks to doing this right and I’ll walk you through them but first let’s establish a few things. The .BuildServer directory is where the magic happens, the TeamCity directory is where the web server and the BuildAgent installers and plugins are located. A BuildAgent can be installed on any remote machine, just make sure you have enough licenses to connect with the BuildServer.
.BuildServer directory is by default "C:\Users\UserName\.BuildServer" and will from now on be referred to as just .BuildServer
- Install TeamCity (no brainer)
- Install configure a database you want to use. The user you are using for teamcity must have dbo access to the database.
- Download the database driver from http://sourceforge.net/projects/jtds/files/
- Unzip "jtds-1.2.5-dist.zi"
- Copy "jtds-1.2.5.jar" to ".BuildServer\lib\jdbc\jtds-1.2.5.jar"
- Navigate to ".BuildServer\config" and create a new file called database.properties
Here is where I got lost for a while. The port for SQL Server needs to be specified and this is not done by default. Further it was really hard to find how to specify the instance name. This is how it should look when it’s correct (using an instance):
connectionUrl=jdbc:jtds:sqlserver://COMPUTERNAME:1433/DATABASENAME/instance=INSTANCENAME;
connectionProperties.user=teamcity
connectionProperties.password=teamcity
maxConnections=50
testOnBorrow=true
This led me up to the next problem saying java is not recognized function or program. After searching the internet for about an hour I gathered that I needed to add environment variables for java so I added JRE_HOME and JAVA_EXE but this didn’t do (pardon the language). There is a quick fix though. Inside "C:\TeamCity\bin" there is a file called "findJava.bat" inside here I just modified the second last statements. This is what my findJava looks like:
:: ---------------------------------------------------------------------
:: Searches for Java executable
:: ---------------------------------------------------------------------
SET JAVA_EXE=%JRE_HOME%\bin\java.exe
IF EXIST "%JAVA_EXE%" goto java_found
SET JAVA_EXE=%JAVA_HOME%\jre\bin\java.exe
IF EXIST "%JAVA_EXE%" goto java_found
SET JAVA_EXE=%JAVA_HOME%\bin\java.exe
IF EXIST "%JAVA_EXE%" goto java_found
SET JAVA_EXE=%TEAMCITY_HOME_DIR%\jre\bin\java.exe
IF EXIST "%JAVA_EXE%" goto java_found
:: ---------------------------------------------------------------------
:: No JAVA_HOME or JRE_HOME set and no bundled JRE found, trying to run java from the PATH
:: ---------------------------------------------------------------------
SET JAVA_EXE="C:\TeamCity\jre\bin\java.exe"
:java_found
Now your TeamCity build server should start working if you have configured your SQL Server instance correctly. I was missing adding to the instance that it should be listening to TCP Port 1433.
And of course your windows firewall might need opening ports if the two are not located on the same machine.
Next I’ll tell you how to move this configuration to another machine should you ever have the need.
by Mikael Henriksson
11. June 2010 00:31
This post is part of a series and the source code can be found at http://github.com/MikaelHenrixon/ConqueringNServiceBus
- Conquering NServiceBus part 1 – Getting Started
- Conquering NServiceBus part 2 – Initial configuration
- Conquering NServiceBus part 3 – A simple Saga
- Conquering NServiceBus part 4 – Testing
- Conquering NServiceBus part 5 – Troubleshooting DTC
- Conquering NServiceBus part 6 – Upgrading StructureMap
I mentioned somewhere that I had to change the StructureMap ObjectBuilder a tiny bit to be able to upgrade and make use of the new API goodness that Jeremy and Joshua had created.
It’s all really simple, I just lifted out the whole project “x:\NServiceBus\src\impl\ObjectBuilder\ObjectBuilder.StructureMap” and made that project a part of my own solution file. I probably don’t have to do that but I did not want to risk accidently overwriting or deleting my changes.
There are only two steps necessary:
- Upgrade StructureMap.dll to the newest version (currently 2.6.1)
- Inside StructureMapObjectBuilder.cs replace:
configuredInstance = x.ForRequestedType(component)
.CacheBy(scope)
.TheDefaultIsConcreteType(component);
with the following
configuredInstance = x.For(component)
.LifecycleIs(scope)
.Use(component);
It’s not even necessary to do this change, a simple replace of the StructureMap.dll in the external-bin folder of the NServiceBus source code should do the trick. It’s been ages ago since I submitted the patch for this but it did not make the 2.0. Starting from the trunk onwards the upgrade should not be necessary.
Now it’s bed time since hours ago :)
by Mikael Henriksson
5. June 2010 23:26
Today I did something brain numbing moving away from NUnit to Machine Specifications. The reason being, it’s a lot nicer from a business perspective. In this case the business is open source and free of charge but it makes more sense. The way mspec forces me to think about the test context and the reason I am at all writing the tests is very useful in a lot of scenarios. For me I am testing various parts of my PBN serializers/readers/writers and everything is context based so MSpec fits like a glove.
This is how the tests looked originally:
namespace Zoolutions.PbnSerialization.Tests
{
[TestFixture]
public class Verify_that_PbnReader_
{
private PbnReaderImpl _reader;
[Test]
public void adds_a_new_board_when_the_Event_line_is_read()
{
const string newBoardLine = @"[Event ""VBK 080114""]";
_reader = new PbnReaderImpl();
_reader.ReadLine(newBoardLine);
_reader.Tournament.Boards.Satisfy(x => x.Count == 1);
}
[Test]
public void can_read_board_info_Annotator()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Annotator ""Växjö BK""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Annotator")
.Value.Satisfy(x => x == "Växjö BK");
}
[Test]
public void can_read_board_info_Application()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Application ""Ruter - Read more at www.brenning.se""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Application")
.Value.Satisfy(x => x == "Ruter - Read more at www.brenning.se");
}
[Test]
public void can_read_board_info_BoardNr()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Board ""1""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Board")
.Value.Satisfy(x => x == "1");
}
[Test]
public void can_read_board_info_Competition()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Competition ""Pairs""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Competition")
.Value.Satisfy(x => x == "Pairs");
}
[Test]
public void can_read_board_info_Contract()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Contract """"]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Contract")
.Value.Satisfy(x => x == string.Empty);
}
[Test]
public void can_read_board_info_Date()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Date ""2008.01.14""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Date")
.Value.Satisfy(x => x == "2008.01.14");
}
[Test]
public void can_read_board_info_Deal()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Deal ""N:T2.QT53.KJ32.AK9 Q53.974.A94.J843 AK9876.AK.76.Q62 J4.J862.QT85.T75""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Deal")
.Value.Satisfy(x => x == "N:T2.QT53.KJ32.AK9 Q53.974.A94.J843 AK9876.AK.76.Q62 J4.J862.QT85.T75");
}
[Test]
public void can_read_board_info_Dealer()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Dealer ""N""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Dealer")
.Value.Satisfy(x => x == "N");
}
[Test]
public void can_read_board_info_Declarer()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Declarer """"]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Declarer")
.Value.Satisfy(x => x == string.Empty);
}
[Test]
public void can_read_board_info_EventDate()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[EventDate ""2008.01.14""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "EventDate")
.Value.Satisfy(x => x == "2008.01.14");
}
[Test]
public void can_read_board_info_North()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[North """"]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "North")
.Value.Satisfy(x => x == string.Empty);
}
[Test]
public void can_read_board_info_Result()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Result """"]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Result")
.Value.Satisfy(x => x == string.Empty);
}
[Test]
public void can_read_board_info_Scoring()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Scoring ""MatchPoints;MP1""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Scoring")
.Value.Satisfy(x => x == "MatchPoints;MP1");
}
[Test]
public void can_read_board_info_Site()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Site ""Växjö BK, Växjö, Sydöstra, Sverige""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Site")
.Value.Satisfy(x => x == "Växjö BK, Växjö, Sydöstra, Sverige");
}
[Test]
public void can_read_board_info_South()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[South """"]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "South")
.Value.Satisfy(x => x == string.Empty);
}
[Test]
public void can_read_board_info_Vulnerable()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[Vulnerable ""None""]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Vulnerable")
.Value.Satisfy(x => x == "None");
}
[Test]
public void can_read_board_info_West()
{
adds_a_new_board_when_the_Event_line_is_read();
const string line = @"[West """"]";
_reader.ReadLine(line);
_reader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "West")
.Value.Satisfy(x => x == string.Empty);
}
[Test]
public void can_read_score_meta()
{
adds_a_new_board_when_the_Event_line_is_read();
_reader.ScoreIsTotal = false;
const string line =
@"[ScoreTable ""Table\2R;Round\2R;PairId_NS\2R;PairId_EW\2R;Contract\4L;Declarer\1R;Result\2R;Lead\3L;Score_NS\5R;Score_EW\5R;MP_NS\4R;MP_EW\4R;Percentage_NS\3R;Percentage_EW\3R""]";
_reader.ReadLine(line);
}
[Test]
public void can_read_score_z()
{
can_read_score_meta();
var line = @" 2 13 9 15 3N N 12 C3 ""490"" - 15.0 1.0 94 6";
_reader.ReadLine(line);
foreach (var pbnBoard in _reader.Tournament.Boards)
{
foreach (var pbnScore in pbnBoard.Scores)
{
pbnScore.RowProperties.Satisfy(prop =>
prop.Single(x => x.Name == "Table").Value == "2" &&
prop.Single(x => x.Name == "Round").Value == "13" &&
prop.Single(x => x.Name == "PairId_NS").Value == "9" &&
prop.Single(x => x.Name == "PairId_EW").Value == "15" &&
prop.Single(x => x.Name == "Contract").Value == "3N" &&
prop.Single(x => x.Name == "Declarer").Value == "N" &&
prop.Single(x => x.Name == "Result").Value == "12" &&
prop.Single(x => x.Name == "Lead").Value == "C3" &&
prop.Single(x => x.Name == "Score_NS").Value == @"490" &&
prop.Single(x => x.Name == "Score_EW").Value == "-" &&
prop.Single(x => x.Name == "MP_NS").Value == "15.0" &&
prop.Single(x => x.Name == "MP_EW").Value == "1.0" &&
prop.Single(x => x.Name == "Percentage_NS").Value == "94" &&
prop.Single(x => x.Name == "Percentage_EW").Value == "6");
}
}
}
[Test]
public void can_read_total_score_meta()
{
_reader = new PbnReaderImpl();
const string totalScoreMeta =
@"[TotalScoreTable ""Rank\2R;RankTie\2R;PairId\2R;Table\2R;Direction\5R;TotalScoreMP\5R;TotalPercentage\5R;Names\40L;NrBoards\2R;Club\25L;MemberID1\7R;MemberID2\7R;Sex1\3R;Sex2\3R""]";
_reader.ReadLine(totalScoreMeta);
var props = _reader.Tournament.TotalScore.MetaProperties;
props.Satisfy(x => x.Count == 14);
}
[Test]
public void can_read_total_score_meta_with_sections()
{
_reader = new PbnReaderImpl();
const string totalScoreMeta =
@"[TotalScoreTable ""Rank\2R;RankTie\2R;PairId\2R;Section\3R;Table\1R;Direction\5R;TotalScoreMP\5R;TotalPercentage\5R;Names\42L;NrBoards\2R;Club\30L;MemberID1\7R;MemberID2\7R;Sex1\3R;Sex2\3R""]";
_reader.ReadLine(totalScoreMeta);
var props = _reader.Tournament.TotalScore.MetaProperties;
props.Satisfy(x => x.Count == 15);
}
[Test]
public void can_read_total_score_z()
{
can_read_total_score_meta();
const string line = @" 1 - 17 4 ""N-S"" 255.0 61.30 ""Ulla Axelsson - Tord Ericsson"" 26 ""Växjö BK"" ""901"" ""915"" ""W"" ""M""";
_reader.ReadLine(line);
var props = _reader.Tournament.TotalScore.Scores;
props.ForEach(score => score.RowProperties.Satisfy(prop => prop.Count == 14));
}
[Test]
public void can_read_tournament_meta()
{
_reader = new PbnReaderImpl();
const string metaLine = @"% <META name=Generator content=""Ruter"">";
_reader.ReadLine(metaLine);
Assert.That(_reader.Tournament.MetaProperties.Count.Equals(1));
var prop = _reader.Tournament.MetaProperties.Single();
prop.Satisfy(x =>
x.Name == "Generator" &&
x.Value == "Ruter");
}
}
After a lot of playing around with MSpec I ended up with the following which allows my test subjects to grow more independently of each other.
[Subject("PbnReaderImpl")]
public class read_score_table : PbnReaderContext
{
Because of = () =>
{
PbnReader.ReadLine(EventLine);
PbnReader.ScoreIsTotal = true;
PbnReader.ReadLine(ScoreTableLine);
};
It should_not_be_score_is_total = () =>
{
PbnReader.ScoreIsTotal.ShouldBeFalse();
PbnReader.Tournament.Boards.Last().MetaProperties.Count.ShouldEqual(14);
};
}
[Subject("PbnReaderImpl")]
public class read_total_score_table_with_sections : PbnReaderContext
{
Because of = () =>
{
PbnReader.ReadLine(EventLine);
PbnReader.ScoreIsTotal = true;
PbnReader.ReadLine(TotalScoreMetaWithSectionLine);
PbnReader.ReadLine(TotalScoreLine);
};
It should_be_score_is_total = () =>
{
PbnReader.ScoreIsTotal.ShouldBeTrue();
};
It should_have_15_meta_properties = () =>
{
ISet<MetaProperty> props = PbnReader.Tournament.TotalScore.MetaProperties;
props.Count.ShouldEqual(15);
};
}
[Subject("PbnReaderImpl")]
public class read_total_score_table : PbnReaderContext
{
Because of = () =>
{
PbnReader.ReadLine(EventLine);
PbnReader.ScoreIsTotal = false;
PbnReader.ReadLine(TotalScoreMetaWithSectionLine);
};
It should_be_score_is_total = () =>
{
PbnReader.ScoreIsTotal.ShouldBeTrue();
};
It should_have_14_meta_properties = () =>
{
ISet<MetaProperty> props = PbnReader.Tournament.TotalScore.MetaProperties;
props.Count.ShouldEqual(15);
};
It each_board_should_have_14meta_properties_with_values = () =>
{
ISet<PbnScore> props = PbnReader.Tournament.TotalScore.Scores;
props.ForEach(score => score.RowProperties.Count.ShouldEqual(14));
};
}
[Subject("PbnReaderImpl")]
public class read_score_table : PbnReaderContext
{
Because of = () =>
{
PbnReader.ReadLine(EventLine);
PbnReader.ScoreIsTotal = true;
PbnReader.ReadLine(ScoreTableLine);
};
It should_not_be_score_is_total = () =>
{
PbnReader.ScoreIsTotal.ShouldBeFalse();
PbnReader.Tournament.Boards.Last().MetaProperties.Count.ShouldEqual(14);
};
}
[Subject("PbnReaderImpl")]
public class read_total_score_table_with_sections : PbnReaderContext
{
Because of = () =>
{
PbnReader.ReadLine(EventLine);
PbnReader.ScoreIsTotal = true;
PbnReader.ReadLine(TotalScoreMetaWithSectionLine);
PbnReader.ReadLine(TotalScoreLine);
};
It should_be_score_is_total = () =>
{
PbnReader.ScoreIsTotal.ShouldBeTrue();
};
It should_have_15_meta_properties = () =>
{
ISet<MetaProperty> props = PbnReader.Tournament.TotalScore.MetaProperties;
props.Count.ShouldEqual(15);
};
}
[Subject("PbnReaderImpl")]
public class read_total_score_table : PbnReaderContext
{
Because of = () =>
{
PbnReader.ReadLine(EventLine);
PbnReader.ScoreIsTotal = false;
PbnReader.ReadLine(TotalScoreMetaWithSectionLine);
};
It should_be_score_is_total = () =>
{
PbnReader.ScoreIsTotal.ShouldBeTrue();
};
It should_have_14_meta_properties = () =>
{
ISet<MetaProperty> props = PbnReader.Tournament.TotalScore.MetaProperties;
props.Count.ShouldEqual(15);
};
It each_board_should_have_14meta_properties_with_values = () =>
{
ISet<PbnScore> props = PbnReader.Tournament.TotalScore.Scores;
props.ForEach(score => score.RowProperties.Count.ShouldEqual(14));
};
}
[Subject("PbnReaderImpl")]
public class read_meta_lines : PbnReaderContext
{
Because of = () =>
{
PbnReader.ReadLine(MetaLine);
PbnReader.ReadLine(EventLine);
PbnReader.ReadLine(AnnotatorLine);
PbnReader.ReadLine(ApplicationLine);
PbnReader.ReadLine(BoardLine);
PbnReader.ReadLine(CompetitionLine);
PbnReader.ReadLine(ContractLine);
PbnReader.ReadLine(DateLine);
PbnReader.ReadLine(DealLine);
PbnReader.ReadLine(DealerLine);
PbnReader.ReadLine(DeclarerLine);
PbnReader.ReadLine(EventDateLine);
PbnReader.ReadLine(NorthLline);
PbnReader.ReadLine(ResultLine);
PbnReader.ReadLine(ScoringLine);
PbnReader.ReadLine(SiteLine);
PbnReader.ReadLine(VulnerableLine);
PbnReader.ReadLine(SouthLine);
PbnReader.ReadLine(WestLine);
PbnReader.ReadLine(EastLine);
};
It should_observation = () =>
{
PbnReader.Tournament.MetaProperties.Count.ShouldEqual(1);
MetaProperty prop = PbnReader.Tournament.MetaProperties.Single();
prop.Name.ShouldEqual("Generator");
prop.Value.ShouldEqual("Ruter");
};
It should_add_a_new_board_if_event_line_is_Read = () =>
{
PbnReader.Tournament.Boards.Count().ShouldBeGreaterThan(0);
};
It should_read_annotator = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Annotator")
.Value.ShouldEqual("Växjö BK");
};
It should_read_application_property = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Application")
.Value.ShouldEqual("Ruter - Read more at www.brenning.se");
};
It should_read_board_info_date = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Date")
.Value.ShouldEqual("2008.01.14");
};
It should_read_board_number = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Board")
.Value.ShouldEqual("1");
};
It should_read_competition = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Competition")
.Value.ShouldEqual("Pairs");
};
It should_read_contract = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Contract")
.Value.ShouldEqual(string.Empty);
};
It should_read_deal = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Deal")
.Value.ShouldEqual("N:T2.QT53.KJ32.AK9 Q53.974.A94.J843 AK9876.AK.76.Q62 J4.J862.QT85.T75");
};
It should_read_dealer = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Dealer")
.Value.ShouldEqual("N");
};
It should_read_declarer = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Declarer")
.Value.ShouldEqual(string.Empty);
};
It should_read_east = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "East")
.Value.ShouldEqual(string.Empty);
};
It should_read_event_date = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "EventDate")
.Value.ShouldEqual("2008.01.14");
};
It should_read_north = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "North")
.Value.ShouldEqual(string.Empty);
};
It should_read_result = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Result")
.Value.ShouldEqual(string.Empty);
};
It should_read_scoring = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Scoring")
.Value.ShouldEqual("MatchPoints;MP1");
};
It should_read_site = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Site")
.Value.ShouldEqual("Växjö BK, Växjö, Sydöstra, Sverige");
};
It should_read_south = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "South")
.Value.ShouldEqual(string.Empty);
};
It should_read_vulnerable = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "Vulnerable")
.Value.ShouldEqual("None");
};
It should_read_west = () =>
{
PbnReader.Tournament.Boards.First()
.BoardProperties.Single(x => x.Name == "West")
.Value.ShouldEqual(string.Empty);
};
}
I think the later looks much nicer than the previous and most important i outputs much nicer to TeamCity. Any “foo” can read what the MSpec runner outputs to both consoles, resharper and TeamCity.
by Mikael Henriksson
5. June 2010 18:32
I admit it does not work for everything and only in C# 4 but it’s a useful extension none the less.
public static IEnumerable<T> RemoveDuplicates<T>(this IEnumerable<T> items)
{
return new HashSet<T>(items);
}
The hash set does not allow duplicates!
a8e7515c-2efd-4e6c-85a1-4d979ba9e956|1|5.0
Tags: linq, C#
LINQ | C#