Latest Posts
It appears that Apple have removed one of the ways to charge iPods and iPhone in the latest generation (3GS iPhone and 3rd Gen iPod Touch). Previous version have allowed charging through either the USB (5V) or Firewire (12V) connections in the standard 30 Pin cable. Now only USB is supported.
The result is that any device that uses the Firewire to charge will no longer charge. This includes Speaker Docks, In Car Adapters and Mains Chargers.
I have personal experience of speaker docks (Klipsch) that are advertised as supporting charging on all 30 Pin connectors that do not charge, obviously attempting to charge over Firewire!
There is a solution available from Apple. It is called a Scosche Passport. There are various available to fit different applications (Docks, In Car).
At iMeta, we use a source control plugin inside Visual Studio 2008, that allows us to associate checkins with tasks and stories from our agile/scrum management tool, Agility. This tool was developed internally, and uses the Visual Studio 2008 checkin policy framework. For those unfamiliar with VS source control checkin policy configuration, it is done by going to the registry hive at:
HKLM\Software\Microsoft\VisualStudio\9.0\TeamFoundation\SourceControl\Checkin Policies
Create a new string registry key, with the name of the tool, and the value of the absolute path to the tool assembly.
However, when we migrated from a pure 32-bit environment to a mixture of 32- and 64-bit machines, we found that on the x64 machines, the tool would not work. Instead, Visual Studio would display the following error in the Policy Warnings tab of the Pending Changes window: "Internal error in <tool name>". By attaching one instance of VS to the instance of VS that attempts to open the tool, we could break when the checkin policy manager attempted to load our tool. This showed us that the assembly reference was an empty string.
After double checking that the assembly reference was correctly added in the value of the new registry key as explained above, it occurred to us that we were running VS as a 32 bit process, using the WoW subsystem. This allows 32-bit apps to run natively on an x64 OS, but under Windows 7 and Vista, it has a separate program files folder, separate .NET framework folder, and also a different registry hive. Sure enough, the plugin worked once we added an identical new registry key to the correct location within the WoW registry hive:
HKLM\Software\Wow6432Node\Microsoft\VisualStudio\9.0\TeamFoundation\SourceControl\Checkin Policies
Remember, ensure you are using the correct Program Files folder, .NET framework folder, or registry hive when running x86 apps on an x64 OS:
| Location Type |
x64 Application |
x86 Application |
| Program Files |
C:\Program Files |
C:\Program Files (x86) |
.NET Framework
|
C:\Windows\Microsoft.NET\Framework64 |
C:\Windows\Microsoft.NET\Framework |
| Registry Hive |
HKLM\Software\ |
HKLM\Software\Wow6432Node\ |
I’ve recently been building a fresh test environment to mimic a live environment as closely as possible using the virtualisation technology that we have here. My design goals are:
- The test environment should have the same network topology as live
- The test web servers should be load balanced the same as the live servers
- The resulting set of machines should be able to be cloned in order to produce multiple test environments
- This has to work on both Hyper-V and VMWare Infrastructure.
The complexity in the solution stems from the fact that this particular solution has a number of SSL websites which are served up by a pair of web servers, SSL sites cannot share an IP address like non-encrypted sites can so there are a large number of IP addresses and load balancing being done.
I’m going to build a Linux server to act as a router and load balancer between my virtualised test environment and the real world, hopefully by using Linux for this piece I can get this working whilst using as few resources as possible on my virtualisation platform and get it up and running quickly and cheaply.
Here’s the environment I’m aiming for:
This article is mainly an aide-mémoire for me as I’ve built a few of these systems now, and each time it takes me half a day to remember how to configure each of the steps, so here’s how I went about building it.
Firstly download Ubuntu 9.10 Server from Ubuntu’s download site. Create a new virtual machine and add two network adapters to it – attach the first adapter (which will become eth0) to the main network and attach the second adapter to your internal network (which will become eth1):
Start the install and choose the default server install. The kernel that gets installed needs to have support for IPVS, the minimum install option (underneath the F4 option) does not have this support in the kernel so I went for the standard install instead. Work through the setup and don’t add any extra software to the server when it asks what packages to add. Once it’s installed and rebooted, we need to log in and install ipvsadm
$ sudo apt-get install ipvsadm
That’s the only extra piece of software we need, so now we just need to configure it.
Switch To Static IP Address
The server is by default installed with a dynamic DHCP IP address, to save any issues with the server being allocated a different address in the future I’ve had a set of addresses on the network reserved and can now assign a static IP address to this machine.
To change the configuration of the network interface we need to edit /etc/network/interfaces, by default it will look like
auto eth0
iface eth0 inet dhcp
To change it to use a static IP address the networking information can be inserted here
auto eth0
iface eth0 inet static
address 172.21.5.200
netmask 255.255.248.0
To get changes that are made to the interfaces file applied we can restart the network:
$ sudo /etc/init.d/networking restart
We can also configure the network interface for the internal network to have the 192.168.1.1 address:
auto eth0
iface eth0 inet static
address 172.21.5.200
netmask 255.255.248.0
auto eth1
iface eth1 inet static
address 192.168.1.1
netmask 255.255.255.0
At this point we can configure the internal windows servers to use this machine as their default gateway:
Configuring Routing/NAT
We now need to teach it to pass traffic between it’s two network interfaces, one on the internal virtual network and the other on the physical network. For this to work IP Forwarding must be enabled in the Kernel, to enable it you run
# sysctl -w net.ipv4.ip_forward=1
This is not a persistent change though, to make the change permanent make the corresponding change in /etc/sysctl.conf, the following will be presented but commented out:
net.ipv4.ip_forward=1
After making the change to sysctl.conf run the following to pick up the change:
# sysctl –p /etc/sysctl.conf
The virtual network is on eth1 with the physical network on eth0, this will allow all the virtual machine to send traffic to the physical network:
# iptables –A FORWARD –i eth1 –j ACCEPT
# iptables –A FORWARD –o eth0 –j ACCEPT
However, in our environment we require the router to perform network address translation as the addresses on the virtual network are not routable from the physical network. So also:
# iptables –t nat –A POSTROUTING –o eth0 –j MASQUERADE
This should then allow the internal virtual machines to ping machines on the physical network. However these settings are not persistent yet, if the router is rebooted this configuration will be lost. So we can save the current configuration to a file and then put that file somewhere handy:
# iptables-save > /etc/iptables.rules
These rules can be loaded when the network interface is brought online by adding an extra line to /etc/network/interfaces:
auto eth0
iface eth0 inet static
address 172.21.5.200
netmask 255.255.248.0
pre-up iptables-restore < /etc/iptables-rules
The routing rules should now be persistent and survive reboots.
Additional IP Addresses
However we need to have multiple IP addresses on the public side of the router so that each one can have an SSL web site associated with it. To add extra addresses to existing interfaces you can create virtual interfaces again by editing /etc/network/interfaces. The definition in the interfaces file now looks like:
auto eth0
iface eth0 inet static
address 172.21.5.200
netmask 255.255.248.0
pre-up iptables-restore < /etc/iptables-rules
auto eth0:0
iface eth0:0 inet static
address 172.21.5.201
netmask 255.255.248.0
Add Load Balancing onto each of the Virtual IPs
Firstly, define the service that is to be exposed load balanced:
# ipvsadm –A –t 172.21.5.201:443 –s rr
The –s rr uses Round Robin scheduling when assigning new connections to servers. Secondly need to add the servers in that are going to handle requests to the service:
# ipvsadm –a –t 172.21.5.201:443 –r 192.168.1.3 –m –w 100
# ipvsadm –a –t 172.21.5.201:443 –r 192.168.1.4 –m –w 100
Again this configuration isn’t permanent, to make these settings apply automatically at boot time the config needs to be placed into /etc/ipvsadm.rules :
# rules file for ipvsadm
–A –t 172.21.5.201:443 –s rr
-a –t 172.21.5.201:443 –r 192.168.1.3 –m –w 100
–a –t 172.21.5.201:443 –r 192.168.1.4 –m –w 100
This can then be rinsed and repeated for eth0:1, eth0:2 etc depending on how many addresses you need.
So that’s it - we’re good to go, we can now deploy our test environment onto the virtual servers and get on with the testing. Users on the main networks can point a browser at https://172.21.5.200 and https://172.21.5.201 (which will get a nice DNS names assigned to them) and the router and load balancer will forward the requests to the virtual server farm.
Remember :
$ man
is your friend :)
For an ASP.Net MVC solution, I wanted to write some unit tests for a controller action method that made use of Controller.UpdateModel(), and was surprised by the paucity of information on the topic. Whilst there is nothing revolutionary here, it may prove useful to someone. The key to the test is managing to supply the values of a posted FORM to the action method, whilst the signature of the method itself takes only a single identifier, such as an int, as a parameter. As we will see, this is achieved using a FormCollection and the controller’s ValueProvider property.
Defining the View and Controller
To provide some context for this example, we have a form that allows us to update a product. A product has a name, colour and id. The form might look something like:
<%using(Html.BeginForm("UpdateProduct"))
{%>
<p>
<label for="Name">Product Name:</label>
<%=Html.TextBox("Name")%>
</p>
<p>
<label for="Colour">Product Colour:</label>
<%=Html.TextBox("Colour")%>
</p>
<%=Html.Hidden("Id")%>
<input type="submit" value="Update" />
<%}%>
Our controller is pretty simple: it has an IProductProvider that will allow us to retrieve products, and a action method called UpdateProduct that is the action invoked by the above form.
public class ProductController : Controller
{
private readonly IProductProvider _productProvider;
public ProductController(IProductProvider productProvider)
{
_productProvider = productProvider;
}
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult UpdateProduct(int id)
{
var product = _productProvider.GetExistingProduct(id);
UpdateModel(product);
return View(product);
}
}
The UpdateProduct method finds the product that matches the supplied id, and then updates the model with the Name and Colour values submitted in the form.
For completeness, the IProductProvider and Product are defined as follows:
public interface IProductProvider
{
Product GetExistingProduct(int id);
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Colour { get; set; }
}
Testing UpdateModel()
In order to test UpdateModel, the test is going to need an HttpContext. I used Rhino Mocks to create stubs for HttpContextBase and the controller’s IProductProvider :
[TestClass]
public class ProductControllerTests
{
// Stubs
private HttpContextBase _stubHttpContext;
private IProductProvider _stubProductProvider;
[TestInitialize]
public void Init()
{
_stubHttpContext = MockRepository.GenerateStub<HttpContextBase>();
_stubProductProvider = MockRepository.GenerateStub<IProductProvider>();
}
}
Finally, the crux of this blog post: creation of the AAA test:
- Create two instances of Product, one to be returned by our call to IProductProvider.GetExistingProduct and another containing the new details supplied in the FORM’s POST.
- Prepare the IProductProvider.GetExistingProduct so that, when called, it returns the existing product.
- Initialise the ProductController, supplying the HttpContext stub.
Now we come to the key of the whole test: setting up the controller with the FORM values that would usually be posted by the View. This can be done by creating a FormCollection that contains our test data and converting this into a type that we can assign to the ValueProvider property on the controller:
controller.ValueProvider = new FormCollection(
new NameValueCollection()
{
{"Name", newProduct.Name},
{"Colour", newProduct.Colour},
{"Id", newProduct.Id.ToString()}
}
).ToValueProvider();
All that is left to do is complete the Act and Assert. The full test looks like this:
[TestMethod]
public void ProductControllerTest_UpdateProduct_Expect_ProductModelUpdated()
{
/***************
* Arrange.
***************/
//Define a couple of instances of Product.
var originalProduct = new Product() { Id = 2, Colour = "Red", Name = "Roger" };
var newProduct = new Product() { Id = originalProduct.Id, Colour = "Green", Name = "George" };
//When GetExistingProduct is called, return the originalProduct instance of Product.
_stubProductProvider.Stub(p => p.GetExistingProduct(Arg<int>.Is.Anything))
.Return(originalProduct);
//Create an instance of the ProductController, using our HttpContext stub.
var controller = new ProductController(_stubProductProvider);
controller.ControllerContext = new ControllerContext(_stubHttpContext, new RouteData(), controller);
//Provide the controller with the test values in lieu of a genuine POST of a FORM.
//We want to simulate the user submitting the details we defined in newProduct.
controller.ValueProvider = new FormCollection(
new NameValueCollection()
{
{"Name", newProduct.Name},
{"Colour", newProduct.Colour},
{"Id", newProduct.Id.ToString()}
}
).ToValueProvider();
/***************
* Act.
***************/
ViewResult viewResult = controller.UpdateProduct(newProduct.Id);
/***************
* Assert.
***************/
var actualModel = viewResult.ViewData.Model;
Assert.IsInstanceOfType(actualModel, typeof(Product));
Assert.AreEqual(newProduct.Colour, ((Product)actualModel).Colour);
Assert.AreEqual(newProduct.Id, ((Product)actualModel).Id);
Assert.AreEqual(newProduct.Name, ((Product)actualModel).Name);
}
A common scenario in web development is the desire to have multiple forms on the same page, some with similar field names. Those coming from an ASP.Net background may not have had to give this scenario much thought before as the use of validation groups and ASP.Net’s rendering of control names and ids make it easy to ensure that the validation messages are associated with the expected elements. When using ASP.Net MVC there are a couple of simple tricks that can be used to ensure that validation errors highlight the correct fields and the appropriate ValidationSummary is used.
An example of a view with multiple forms is a page that displays both logon and registration forms on the same page: both forms may require user name and password fields, such as the one shown below:
The first stab at writing the mark-up might look something like:
<%using (Html.BeginForm("Logon", "Logon"))
{%>
<fieldset>
<legend>Logon</legend>
<%=Html.ValidationSummary() %>
<label for="UserName">User Name:</label>
<%=Html.TextBox("UserName") %>
<label for="Password">Password:</label>
<%=Html.Password("Password") %>
</fieldset>
<input type="submit" value="Logon" />
<%} %>
<%using (Html.BeginForm("Register", "Logon"))
{%>
<fieldset>
<legend>Register</legend>
<%=Html.ValidationSummary()%>
<label for="UserName">User Name:</label>
<%=Html.TextBox("UserName")%>
<label for="Password">Password:</label>
<%=Html.Password("Password")%>
<label for="Forename">Forename:</label>
<%=Html.Password("Forename")%>
<label for="Surname">Surname:</label>
<%=Html.Password("Surname")%>
<label for="EmailAddress">Email Address:</label>
<%=Html.Password("EmailAddress")%>
</fieldset>
<input type="submit" value="Register" />
<%} %>
The corresponding controller may be along the lines of:
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Logon(CredentialsModel model)
{
NameValueCollection validationErrors = _authenticationService.Logon(model);
return CredentialsAreValid(validationErrors) ?
View("Home") :
View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Register(CredentialsModel model)
{
NameValueCollection validationErrors = _authenticationService.Register(model);
return CredentialsAreValid(validationErrors) ?
View("Home") :
View("Logon", model);
}
private bool CredentialsAreValid(NameValueCollection validationErrors)
{
foreach (string fieldName in validationErrors)
{
ModelState.AddModelError(fieldName, HttpUtility.HtmlEncode(validationErrors[fieldName]));
}
return validationErrors.Count == 0;
}
Both calls to the _authenticationService return a NameValueCollection where the key is the name of the property (ie control) that failed validation and the value is the error message. By way of example, the Logon method on the AuthenticationService test stub always returns sample user name and password validation errors within a NameValueCollection.
public NameValueCollection Logon(CredentialsModel model)
{
return new NameValueCollection
{
{"UserName", "User name must be a minimum of 8 characters."},
{"Password", "The password must contain both numbers and letters."}
};
}
For completeness, the CredentialsModel is as follows:
public class CredentialsModel
{
public string UserName { get; set; }
public string Password { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
}
Note that we want to have the same names for both logon and password elements in the view as we wish to use the same CredentialsModel as the parameter of both the Register and Logon actions.
When submitting valid data in either form, the correct data is passed to the correct action, and all appears well and good. The shortcomings of this code so far only become apparent if one of the fields fail validation. For example, if the user was to attempt to logon without entering a user name or password, the UserName and Password elements in both the logon and registration section are highlighted (as they have the same name) and both validation summaries display the error messages:
We have two problems:
- Multiple elements with the same name;
- Multiple validation summaries with no way of distinguishing between them.
View with multiple elements with the same name
We can deal with the problem of multiple elements with the same name (which we need to have so we can use the same model for both of our actions) by taking advantage of the way ASP.Net MVC’s DefaultModelBinder deals with prefixes, inferring the prefix from the method parameter name. First, we prefix the element names in each form with a different prefix: in this case adding the logon and registration prefix as appropriate. Now the UserName element in the logon form has the name logon.UserName, whilst the UserName element in the registration form is now registration.UserName. Our view now looks like this:
<%using (Html.BeginForm("Logon", "Logon"))
{%>
<fieldset>
<legend>Logon</legend>
<%=Html.ValidationSummary() %>
<label for="logon_UserName">User Name:</label>
<%=Html.TextBox("logon.UserName") %>
<label for="logon_Password">Password:</label>
<%=Html.Password("logon.Password")%>
</fieldset>
<input type="submit" value="Logon" />
<%} %>
<%using (Html.BeginForm("Register", "Logon"))
{%>
<fieldset>
<legend>Register</legend>
<%=Html.ValidationSummary()%>
<label for="registration_UserName">User Name:</label>
<%=Html.TextBox("registration.UserName")%>
<label for="registration_Password">Password:</label>
<%=Html.Password("registration.Password")%>
<label for="registration_Forename">Forename:</label>
<%=Html.Password("registration.Forename")%>
<label for="registration_Surname">Surname:</label>
<%=Html.Password("registration.Surname")%>
<label for="registration_EmailAddress">Email Address:</label>
<%=Html.Password("registration.EmailAddress")%>
</fieldset>
<input type="submit" value="Register" />
<%} %>
Note that whilst the name of the input elements (the TextBox and Password controls) use a full-stop (“.”) as the separator between the prefix and the rest of the name, the label needs to use an underscore (“_”) when identifying the id of the element with which the label is associated (via the for property), as this is the separator the ASP.Net MVC renders when it creates the id of the element. The Action’s model parameter binds to the element’s name, whilst the HTML label identifies the element using its id.
We also need to update the parameter names in our actions so that the match our prefix:
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Logon(CredentialsModel logon)
{
NameValueCollection validationErrors = _authenticationService.Logon(logon);
return CredentialsAreValid(validationErrors, "logon") ?
View("Home") :
View(logon);
}
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Register(CredentialsModel registration)
{
NameValueCollection validationErrors = _authenticationService.Register(registration);
return CredentialsAreValid(validationErrors, "registration") ?
View("Home") :
View("Logon", registration);
}
private bool CredentialsAreValid(NameValueCollection validationErrors, string prefix)
{
foreach (string fieldName in validationErrors)
{
//Concatenate the prefix with the model property name (as stored in the key) to identify
//the name of the HTML element on the page.
ModelState.AddModelError(prefix + "." + fieldName, HttpUtility.HtmlEncode(validationErrors[fieldName]));
}
return validationErrors.Count == 0;
}
}
Our Logon action now takes a CredentialsModel parameter called logon, whilst the Register action now has a parameter called registration. This on its own won’t solve our validation problem: we also updated our call to ModelState.AddModelError within the method CredentialsAreValid, concatenating the appropriate prefix to the field name, using the full-stop separator.
Should we wish to be more explicit, or wish to use a parameter name that differs from the prefix, we can identify the prefix in the Action’s signature using the Bind attribute:
public ViewResult Logon([Bind(Prefix="logon")]CredentialsModel model)
Now when we attempt to logon without supplying a user name or password, only the elements that have failed validation are highlighted:
However, we still have a problem with the ValidationSummary, which we will deal with next.
Multiple ValidationSummary controls contained within the same page
In order to host multiple validation summaries on the same form, and be able to distinguish between each one, we adapted the Html.MyValidationSummary extension described on stackoverflow. In essence, we use ViewData to identify which ValidationSummary we wish to use to display our error messages.
First, create an extension to HtmlHelper called NamedValidationSummary, which takes a string parameter called name:
public static class HtmlExtensions
{
public static readonly string FormNameKey = "FormName";
public static string NamedValidationSummary(this HtmlHelper html, string name)
{
if (html.ViewData[FormNameKey] != null
&& !string.IsNullOrEmpty(html.ViewData[FormNameKey].ToString())
&& (html.ViewData[FormNameKey].ToString() == name))
{
return html.ValidationSummary();
}
return "";
}
}
We then update our view to use the NamedValidationSummary, supplying the appropriate prefix for the associated form as the name parameter:
<%using (Html.BeginForm("Logon", "Logon"))
{%>
<fieldset>
<legend>Logon</legend>
<%=Html.NamedValidationSummary("logon") %>
<label for="logon_UserName">User Name:</label>
<%=Html.TextBox("logon.UserName") %>
<label for="logon_Password">Password:</label>
<%=Html.Password("logon.Password") %>
</fieldset>
<input type="submit" value="Logon" />
<%} %>
<%using (Html.BeginForm("Register", "Logon"))
{%>
<fieldset>
<legend>Register</legend>
<%=Html.NamedValidationSummary("registration")%>
<label for="registration_UserName">User Name:</label>
<%=Html.TextBox("registration.UserName")%>
<label for="registration_Password">Password:</label>
<%=Html.Password("registration.Password")%>
<label for="registration_Forename">Forename:</label>
<%=Html.Password("registration.Forename")%>
<label for="registration_Surname">Surname:</label>
<%=Html.Password("registration.Surname")%>
<label for="registration_EmailAddress">Email Address:</label>
<%=Html.Password("registration.EmailAddress")%>
</fieldset>
<input type="submit" value="Register" />
<%} %>
Finally, update the CredentialsAreValid method within the controller to set the ViewData item that is used by our NamedValidationSummary (in this case, the value of HtmlExtensions.FormNameKey which we defined as "FormName" within our HtmlExtensions class) to the name we gave the the relevant NamedValidationSummary in our view, which in this case is the same as the form’s prefix:
private bool CredentialsAreValid(NameValueCollection validationErrors, string prefix)
{
foreach (string fieldName in validationErrors)
{
//Concatenate the prefix with the model property name (as stored in the key) to identify
//the name of the HTML element on the page.
ModelState.AddModelError(prefix + "." + fieldName, HttpUtility.HtmlEncode(validationErrors[fieldName]));
}
if(validationErrors.Count>0)
{
//We gave our NamedValidationSummary the same id as the prefix to the fields of the
//form we are validating.
ViewData[HtmlExtensions.FormNameKey] = prefix;
}
return validationErrors.Count == 0;
}
We have now managed to limit the displayed validation to the form that has been submitted:

Since my University days I have had a strong passion for sport – in particular rowing. Many of my colleagues at iMeta think I’m mad going down to the river in the wind and rain at 6:30am to train before coming into the office – in fact they would probably call it an obsession.
I took up rowing in 2003, catching the bug hard. I am still competing to this day some 6 years later. I started out in my University’s beginner squad but progressed to the top senior boat within a year or so. I have won BUCS (British University & Colleges Sport) medals in addition to twice competing at Henley Royal Regatta. Since graduating I have moved into a single scull and now coach the beginners and intermediates at the University. I immensely enjoy coaching – finding inventive ways to express and illustrate my knowledge to those who are keen to learn.
I have often wondered whether the techniques I have learnt as an athlete and those that I am now learning as a coach can be applied to mentoring within the professional software development world.
1. Break it down
The rowing stroke is often broken down into it’s constituent parts – these can broadly be described as the drive (oar in the water), finish (oar extracted from the water), recovery (oar out of the water and preparing for the next stroke) and catch (oar re-entering the water at the end of the recovery). The goal here is to enable the coach and athlete to concentrate on a particular problem area.
I remember watching Uncle Bob’s Keynote from NDC2009 – during his presentation he makes references to software developers practising a bubble sort. Sort algorithms in general can be thought of as a key part of a software developer’s toolbox, breaking it down and practising these algorithms (as well as many others, for example encryption algorithms) makes sense.
2. Exaggerate
The idea behind many exercises used to improve an athlete’s rowing stroke is to exaggerate a correction/improvement in isolation. A basic example could be rowing “continuously squared” – that is with the oar perpendicular to the water at all points during the stroke. Rowing “continuously squared” can be used to exaggerate several things, including balance discipline and proper extraction of the oar at the finish of the stroke.
Rather than just practising a bubble sort in one language, could it make sense to exaggerate this and practise it in many, for example C#, F# and Ruby?
3. Distract
There are parts of the rowing stroke that are just plain unnatural, both to newcomers to the sport and experienced athletes alike. An athlete can also develop undesirable habits within their rowing stroke which can prove hard to break. In these scenarios it can be useful for the coach to give the athlete something else to do. For example, newcomers often find it difficult to separate what their hands should be doing on the oar – the inside hand (furthest away from the end of the oar handle) is used to square and feather the oar (square it in preparation for entry to the water and feather it at the finish to minimise air drag on the recovery), whereas the outside hand is used as a lever and stays relaxed whilst the inside hand feathers. In this situation asking the athlete to hover their outside hand 6 inches above the oar handle whilst the inside hand feathers can be a useful method of teaching the athlete not to feather with their outside hand.
For many a software developer Test Driven Development (TDD) is an unnatural approach. The idea of writing tests before a single line of application code may not be easily grasped. However it has numerous advantages, but I would like to highlight only one – producing un-testable application code is impossible with TDD. Imagine asking a junior developer to write application code that is testable – I’m sure many would struggle to achieve this. TDD can be viewed as a structured method of distracting a developer away from things that make code un-testable – by asking them to do the exact opposite, for example dependency injection in the constructor of a class.
4. Practise
Once a change for the better has been made, the athlete should practise it over and over again until it becomes a natural part of their rowing stroke, this can be commonly referred to as muscle memory. A software developer who regularly practises and understands sort algorithms is much more likely to select and implement an appropriate algorithm (in terms of sorting efficiency) for use in scenarios with Live applications.
All rowers (or at least those who want to win) will not practice their stroke during a race when they are at maximum exertion, instead they rely on muscle memory. With this in mind, it makes sense for software developers to practise techniques on experimental rather than Live application code bases.
5. Review
Every once in a while part of an athlete’s rowing stroke will regress – this can often be as a result of the athlete working on something new. In this scenario the coach and athlete will review and practise the exercises that helped the athlete with the original problem.
Taking my own advice somewhat, I recently began practising a binary chop algorithm – I was surprised at how difficult I found this. It was simply because I had not practised a binary chop since my University days. Reviewing a previously mastered technique/technology/algorithm every once in a while is clearly a good thing :)
My experiences and interpretations of Agile development includes the requirement for Agile teams to be composed of experts. This must be very expensive! Would it not be better to have a mix of talented juniors and experienced seniors combined with a knowledgeable mentor? Or should all teams – regardless of composition – include good mentors? I know what I think – how about you?
I know this post is very specific to Windows 7 but I thought it was worth pointing out that some of the little tools embedded in the OS are often the most valuable to us testers (or anyone wanting to recreate an issue). One such tool is the Problem Steps Recorder in Windows 7. It’s a little app that records all of the actions a user performs and then documents them through screen shots and information about the actions. (Note – this is not a record and playback tool)
Open the start menu and type psr in to the search bar.
You should get the psr.exe file returned in your search.
Open the application and you’ll get the Problem Steps Recorder screen as follows:
Now, it’s a simple case of clicking Start Record and then doing your testing, recreating of bug or whatever it is you want to record. This isn’t exclusive to web either – this tool works for anything in your operating system. In my example I’m going to open Internet Explorer and then search for iMeta before browsing through a few pages within the iMeta site before opening up the iMeta twitter page.
After you have finished simply click stop recording and you will be asked to choose a save location. The file that is saved is a zip file, so give it a name and location and hit save.
Now open the zip file.
The zip file contains a .mht file which contains all of the details of your recording session. In my example below it has captured both desktops (we use dual desktops). The document that opens contains screenshots and descriptions of the actions taken. At the end there is a summary of actions too. You can skip to each part or play the screens as a slideshow too.
Here’s a couple of screens taken from the file.

The screen shots don’t do the final report justice. I’d suggest you give it a go to really see the details of the screens. At the end of the file is the following information. I’ve highlighted the test steps in bold/red:
“Recording Session: 12/01/2010 10:27:25 - 10:27:52
Problem Steps: 8, Missed Steps: 0, Other Errors: 0
Operating System: win7_rtm
Problem Step 1: User left click on "Internet Explorer (push button)"
Program: Windows Explorer, (win7_rtm), Microsoft Corporation, EXPLORER.EXE, EXPLORER.EXE
UI Elements: Internet Explorer, Running applications, Running applications, MSTaskListWClass, Running applications, MSTaskSwWClass, ReBarWindow32, Shell_TrayWnd
Problem Step 2: User keyboard input in "Google - Windows Internet Explorer" [... ENTER]
Program: Internet Explorer,(win7_rtm.), Microsoft Corporation, IEXPLORE.EXE SCODEF:CREDAT:, IEXPLORE.EXE
UI Elements: Internet Explorer_Server, Shell DocObject View, Google - Windows Internet Explorer, TabWindowClass, Frame Tab, Google - Windows Internet Explorer, IEFrame
Problem Step 3: User left click on " - Developing software, transforming business (editable text)" in "imeta - Google Search - Windows Internet Explorer"
Program: Internet Explorer, (win7_rtm.), Microsoft Corporation, IEXPLORE.EXE SCODEF:CREDAT:, IEXPLORE.EXE
UI Elements: - Developing software, transforming business, iMeta - Developing software, transforming business, imeta - Google Search, Internet Explorer_Server, Shell DocObject View, imeta - Google Search - Windows Internet Explorer, TabWindowClass, Frame Tab, imeta - Google Search - Windows Internet Explorer, IEFrame
Problem Step 4: User left click on "consultancy service (editable text)" in "iMeta - Developing software, transforming business - Windows Internet Explorer"
Program: Internet Explorer, (win7_rtm.), Microsoft Corporation, IEXPLORE.EXE SCODEF:CREDAT:, IEXPLORE.EXE
UI Elements: consultancy service, consultancy service, iMeta - Developing software, transforming business, Internet Explorer_Server, Shell DocObject View, iMeta - Developing software, transforming business - Windows Internet Explorer, TabWindowClass, Frame Tab, iMeta - Developing software, transforming business - Windows Internet Explorer, IEFrame
Problem Step 5: User left click on "Embedding Agile (editable text)" in "iMeta - Consultancy - Windows Internet Explorer"
Program: Internet Explorer, (win7_rtm.), Microsoft Corporation, IEXPLORE.EXE SCODEF:CREDAT:, IEXPLORE.EXE
UI Elements: Embedding Agile, Embedding Agile, iMeta - Consultancy, Internet Explorer_Server, Shell DocObject View, iMeta - Consultancy - Windows Internet Explorer, TabWindowClass, Frame Tab, iMeta - Consultancy - Windows Internet Explorer, IEFrame
Problem Step 6: User mouse wheel down on "iMeta - Consultancy - Embedding Agile (pane)" in "iMeta - Consultancy - Embedding Agile - Windows Internet Explorer"
Program: Internet Explorer, (win7_rtm.), Microsoft Corporation, IEXPLORE.EXE SCODEF:CREDAT:, IEXPLORE.EXE
UI Elements: iMeta - Consultancy - Embedding Agile, Internet Explorer_Server, Shell DocObject View, iMeta - Consultancy - Embedding Agile - Windows Internet Explorer, TabWindowClass, Frame Tab, iMeta - Consultancy - Embedding Agile - Windows Internet Explorer, IEFrame
Problem Step 7: User left click on "> Follow us on Twitter (editable text)" in "iMeta - Consultancy - Embedding Agile - Windows Internet Explorer"
Program: Internet Explorer, (win7_rtm.), Microsoft Corporation, IEXPLORE.EXE SCODEF:CREDAT:, IEXPLORE.EXE
UI Elements: > Follow us on Twitter, > Follow us on Twitter, iMeta - Consultancy - Embedding Agile, Internet Explorer_Server, Shell DocObject View, iMeta - Consultancy - Embedding Agile - Windows Internet Explorer, TabWindowClass, Frame Tab, iMeta - Consultancy - Embedding Agile - Windows Internet Explorer, IEFrame
Problem Step 8: User left double click in "iMeta Technologies (iMeta_UK) on Twitter - Windows Internet Explorer"
Program: Internet Explorer, (win7_rtm.), Microsoft Corporation, IEXPLORE.EXE, IEXPLORE.EXE
UI Elements: iMeta Technologies (iMeta_UK) on Twitter - Windows Internet Explorer, IEFrame “
As you can see the final file includes almost all of the information you would need to recreate any potential defects or scenarios. Along with the screen shots the information gives a complete picture, which can often be missing from many bug reports.
If you’ve got Windows 7 installed then try using the PSR.exe tool and see if you’d find it valuable.
Also let me know, via the comments (or twitter @rob_lambert) of any other awesome apps that come bundled with the OS. Shout out to Steve S for pointing me to the tool.
Enjoy.
I’ve spent a bit of time over the last few day’s playing with the IIS Search Engine Optimization Toolkit, whose workings are described on ScottGu’s Blog. In addition to providing all sorts of helpful SEO tips, it also proved to be a useful diagnostic tool, helping to identify bugs in the website such as broken links or poor HTML mark-up (such as pages containing two title elements as they were set in two places in the code). It proved of particular use when dealing with the ASP.Net MVC Outgoing Routing ActionLink mismatch problem I was trying to resolve, highlighting the need for an HTTP 301 permanent redirect for some legacy URLs.
I’ve recently had a great experience using AutoMapper on a project (the project is iMeta’s solution to managing information used to settle trades, Assassin).
I used it to map from my service contract back into the domain model of our application.
There’s a bunch of really good reasons (e.g. ease of use and protecting people from change) why a service contract is not the same as your internal data model. There are also technical reasons, for example, you don’t want to litter your internal data model with WCF attributes. So you end up having a bunch of classes that represent your service contract that are similar to but not the same as your internal data model. Therefore, there is overhead maintaining the mappings between those two object models – this is where AutoMapper helps. AutoMapper also helped reduce the amount of code we had to write to start off with, but that is a one off cost and doesn’t really take that long to write (even though after 30 mins of a.x = b.y does make you want to pull out your eyes). The biggest win is maintenance by far.
If you’re in control of your contract / object model it is a good idea to make life as easy for yourself as possible. For example, try and make property names match – hopefully they naturally will. That way AutoMapper will map, by convention, most of your properties automatically. 80% of what you need for 20% of the effort!
You can download AutoMapper via Horn, which I find the easiest way to get the latest version of open source binaries!
To help illustrate what I’m talking about I’ll finish with an example. I haven’t shown the projects usage (it wouldn’t make sense unless you’d like to first understand trade settlement data) but here’s an example (contrived a bit):
1: // setup the mapper
2: Mapper.CreateMap<ServiceContract.Customer, Domain.Customer>()
3: .ForMember(c => c.Products, opt => opt.MapFrom(sc => sc.InterestedIn));
4: Mapper.CreateMap<ServiceContract.Product, Domain.Product>()
5: .AfterMap((contractProduct, domainProduct)
6: => domainProduct.ProductType = LookupProductType(contractProduct.Name));
7: Mapper.CreateMap<ServiceContract.Address, Domain.Address>();
8:
9: // setup some data
10: var serviceCustomer = new ServiceContract.Customer
11: {
12: Firstname = "John",
13: Surname = "Smith",
14: DateOfBirth = new DateTime(1980, 10, 10),
15: Address = new ServiceContract.Address {HouseName = "1", Postcode = "postcode"}
16: };
17: serviceCustomer.InterestedIn.Add(new ServiceContract.Product{Name = "Something for the wife"});
18:
19: // map it
20: var domainCustomer = new Domain.Customer();
21: Mapper.Map(serviceCustomer, domainCustomer);
22:
23: private static ProductType LookupProductType(string name)
24: {
25: // do some db lookup maybe
26: }
As you can see AutoMapper can also update an instance object, so if someone wanted to provide updates to existing objects then it’s a piece of cake too!
Here are the classes which are being mapped:
1: namespace Domain
2: {
3:
4: public class Customer
5: {
6: public Customer()
7: {
8: Products = new ProductCollection();
9: ContactHistory = new ContactHistory();
10: }
11:
12: public string Firstname { get; set; }
13: public string Surname { get; set; }
14: public DateTime DateOfBirth { get; set; }
15: public Address Address { get; set; }
16: public string EmailAddress { get; set;}
17:
18: public ProductCollection Products { get; private set; }
19:
20: // extra stuff on customer that you don't want third parties to provide
21: public ContactHistory ContactHistory { get; private set; }
22: // .....
23: }
24:
25: public class ContactHistory
26: {
27: }
28:
29: public class Address
30: {
31: public string HouseName {get; set;}
32: public string Postcode { get; set; }
33: }
34:
35:
36: public class ProductCollection : List<Product>
37: { }
38:
39: public class Product
40: {
41: public ProductType ProductType { get; set; }
42: }
43:
44: public class ProductType
45: {
46: public string Name { get; set; }
47: }
48: }
49:
50: namespace ServiceContract
51: {
52: [DataContract]
53: public class Customer
54: {
55: public Customer()
56: {
57: InterestedIn = new List<Product>();
58: }
59:
60: [DataMember]
61: public string Firstname { get; set; }
62: [DataMember]
63: public string Surname { get; set; }
64: [DataMember]
65: public DateTime DateOfBirth { get; set; }
66: [DataMember]
67: public Address Address { get; set; }
68: [DataMember]
69: public string EmailAddress { get; set; }
70: [DataMember]
71: public List<Product> InterestedIn { get; set; }
72: }
73:
74: [DataContract]
75: public class Product
76: {
77: [DataMember]
78: public string Name { get; set; }
79: }
80:
81: [DataContract]
82: public class Address
83: {
84: [DataMember]
85: public string HouseName { get; set; }
86: [DataMember]
87: public string Postcode { get; set; }
88: }
89: }
Some more examples can be found on the codeplex site: http://www.codeplex.com/AutoMapper
Just saw a good little LINQ quiz on Justin Etheredge's blog - I saw it way too late to get the TekPub subscription, but thought it looked fun anyhow. Here's my solution:
static void Main(string[] args)
{
int max = 100;
var primes = Enumerable.Range(1, max)
.Where(i => i > 1)
.Aggregate(Enumerable.Range(2, max - 1).ToArray(), (sieve, i) =>
{
if ((i > Math.Sqrt(max)) || (sieve[i - 2] == 0)) return sieve;
for (int m = 2; m <= max / i; m++)
sieve[i * m - 2] = 0;
return sieve;
})
.Where(n => n != 0)
;
foreach (var p in primes)
{
Console.WriteLine(p);
}
}
Arguably it's not pure LINQ; there's more going on inside the aggregate lambda than I'd like, but it does the job and for those into this sort of thing you should recognise it as an implementation of the Sieve of Eratosthenes.
We recently experienced an unexpected problem when attempting to re-route obsolete URLs using ASP.Net’s MVC routing mechanism. The scenario was that we had been running a competition, and had a set of URLs associated with it in the format
http://server/Competition/etc. We now needed to re-route all incoming requests to these pages back to the site’s home page. In order to achieve this, we added the following to the routing table:
routes.MapRoute(
null,
"Competition/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
In the majority of cases this worked fine. However, a handful of links unrelated to the competition now exhibited strange behaviour. For example, the menu item on the home page that was a link to itself (i.e. the home page) now had the URL http://server/Competition rather than http://server/. The link itself was generated in the view Home/Index.aspx using an ActionLink:
<%= Html.ActionLink("Home", "Index", "Home")%>
It turned out that the problem was with self-referencing ActionLinks where the link matched the controller and action for the given view. The outgoing URLs for these self-referencing ActionLinks now matched the new competition route. The problem was resolved by adding a constraint to the route, ensuring that this route was only applied to incoming requests:
routes.MapRoute(
null,
"Competition/{action}/{id}",
new { controller = "Home", action = "Index", id = "" },
new { routeDirection = new IncomingRequestConstraint() }
);
The new constraint, IncomingRequestConstraint, checks the route direction:
public class IncomingRequestConstraint : IRouteConstraint
{
public bool Match(System.Web.HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection)
{
return routeDirection == RouteDirection.IncomingRequest;
}
}
Post Script: Legacy URLs
The above example is a slightly simplified version of how we dealt with the obsolete URLs. In order to establish that the re-routing was permanent and use an HTTP 301 redirect, we made use of the legacy URL routing described by Matt Hawley at eXcentrics World. Our actual route looked like this:
routes.Add(
null,
new LegacyRoute
(
"Competition/{action}/{id}",
"RedirectHome",
new LegacyRouteHandler()
)
);
As a result, the same outgoing URL now took the form http://server/Competition/Index/?controller=Home. Adding the constraint resulted in:
routes.Add(
null,
new LegacyRoute
(
"Competition/{action}/{id}",
"RedirectHome",
new LegacyRouteHandler()
)
{
Constraints = new RouteValueDictionary(
new { routeDirection = new IncomingRequestConstraint() }
)
}
);
I was having some trouble getting my Humax Freesat receiver (the FOXSAT-HD model) recording two channels at the same time. The reason I was having trouble is due to the fact that when I initially used the device I had only connected it with a single digital input. I later connected both inputs (when I had extra cabling installed). The Humax box detected that both tuners were connected but I could not view all channels when I was recording.
To fix it I had to do a factory reset. Fortunately, when you do this it doesn’t wipe all your recorded stuff (think it wiped my record schedule though but not sure if it’s my bad memory). When the box restarts it takes you through the initial set up and should detect both satellite inputs and tunes them in. I can now record and watch TV at the same time.
I’d still highly recommend the Humax Freesat receiver to anyone looking for such a device. I had the Freeview receiver before I upgraded to Freesat and that was pretty good too!
Hope this helps someone else!
It’s with a bittersweet feeling that I write this post. As of January, I will no longer be with iMeta Technologies. During my time here, I’ve been involved in some awesome projects, had the privilege of working with some very smart and talented people, and made some great friends. I wish you all the best success in the future.
As of January, I'm starting a new job. I’m going to go back to working from home, something I did for 8 years prior to joining iMeta, so for me it feels like home (pun intended). I have to admit that as much as I liked the office buzz and enjoyed every minute of working with Steve and his eternal ramblings on expression trees, I am happy to not face morning and afternoon commutes.
So what’s my new job? Well it comes as no surprise to many to say that I’ve been a fan of a tool called ReSharper for quite some time. I’ve been a member of the JetBrains Academy for a few years and have collaborated with JetBrains at various conference booths. We’ve now decided to formalize the relationship.
I’m very excited to announce that as of January I will be joining JetBrains. My primary role will be to help promote their products on the .NET side, which include ReSharper, dotTrace, as well as cross-platform tools such as TeamCity, YouTrack or WebIDE. As part of my job, I’ll be doing screen casts, blogging, and not only about using the tools, but also focusing on best practices, boosting productivity, etc. As a company that firmly believes in OSS, it also means that I’ll be increasing my participation in OSS projects and actually get to code on things I enjoy!
I’ll be active in the community, continuing to speak at conferences and events, as well as serve as a liaison for JetBrains. I’ll continue to blog on my own site as well as devlicious.
As far as Twitter, we’ll be pushing the Tool-Specific Twitter accounts for ReSharper and dotTrace, so make sure you follow them! You can contact me via my own twitter feed, and of course, don’t forget Ilya, also known as Orangy, ReSharper PM!
With that, all that’s left to say is Happy New Year and we’ll talk in 2010! And don’t start 2010 without ReSharper!
Using the new Linq provider is pretty simple. It all hangs of a Query() extension method on ISession, so you can do things like the following:
from c in session.Query<Customer>() select c
In my tests, I've tended to wrap the session.Query() call behind a simple facade, along the lines of:
public class Northwind
{
private readonly ISession _session;
public Northwind(ISession session)
{
_session = session;
}
public IQueryable<Customer> Customers { get { return _session.Query<Customer>(); }
}
Of course, that's entirely optional, but I find the resulting code easier to read:
from c in db.Customers select c
Once you know how to hook into the session (which as you can see is pretty simple), the rest is just straightforward Linq code, and entirely up to you! Right now I'm not exposing any extension points, but they'll be coming soon (plus another post to describe how to use them).
The version 1 provider used an ISession extension method call Linq() to provide its hook. I purposefully used a different name, since there's no reason at all why you can't use both providers within the same project or, indeed, within the same session. So that gives a couple of migration options for folk that want to move to the new provider:
- Leave all your current queries using .Linq(), and start using .Query() for new ones.
- Start changing existing queries from .Linq() to .Query(), just by changing them or by a simple search & replace. The rest of the expression will (hopefully!) just work.
- Drop your reference to the original Linq provider assembly, and create your own extension method:
public static IQueryable<T> Linq<T>(this ISession session)
{
return session.Query<T>();
}
This lets you switch to the new provider without changing a line of your code - and if you find it all goes to hell, you just re-add the V1 reference and comment out your extension method. Should work like a treat.
Other than that, I don't think there's much to tell - usage really should be pretty simple. Oh, one thing that springs to mind - although you can use the V1 provider and the new provider within the same project (or session), don't try to compose queries from them together; that's really going to do weird stuff!
Time for another progress report, and this one's a biggie :)
Barring a couple of pretty minor things that won't take much fixing, all the original Linq tests have now been ported over to the new provider and are all passing. That means the new provider is now (from the perspective of the tests, at least) in better shape that the version 1 provider. It can do everything the original provider could, plus a whole bunch more. A couple of example queries that now work just fine are:
from e in db.Employees
from et in e.Territories
where e.Address.City == "Seattle"
select new {e.FirstName, e.LastName, et.Region.Description};
from c in db.Customers
join o in db.Orders on c.CustomerId equals o.Customer.CustomerId into orders
select new {c.ContactName, OrderCount = orders.Average(x => x.Freight)};
from o in db.Orders
from p in db.Products
join d in db.OrderLines on new {o.OrderId, p.ProductId} equals new {d.Order.OrderId, d.Product.ProductId}
into details
from d in details
select new {o.OrderId, p.ProductId, d.UnitPrice}
from c in db.Customers
join o in db.Orders on c.CustomerId equals o.Customer.CustomerId
group o by c into x
select new { CustomerName = x.Key.ContactName, Order = x }
(ignore whether those queries make any real sense, it's just the form of them that matters)
So, more importantly, what doesn't work? Well, out of the tests that we've currently got, not a huge amount. Some important areas that are missing are:
- Nested selects to produce hierarchical output. I've got some prototype code for doing this, so it will be supported at some point but isn't there right now. Of course, since NH already understands relationships, nested selects are far less important than they are for something like Linq to SQL.
- Group joins that produce hierarchical output - these essentially boil down to the same code as the nested select case, so support for these will probably come at around the same time. Group joins that don't introduce a hierarchy should work just fine.
- Set operations, such as Union and Intersect. Union you can obviously do yourself in client code with no particular overhead. Intersect would really be better in the provider :)
- Left outer join style queries, such as:
from e in db.Employees
join o in db.Orders on e equals o.Employee into ords
from o in ords.DefaultIfEmpty()
select new {e.FirstName, e.LastName, Order = o};
This is a fairly widely used construct, so is close to top of the list for future support
- Let expressions, such as:
from c in db.Customers
join o in db.Orders on c.CustomerId equals o.Customer.CustomerId into ords
let z = c.Address.City + c.Address.Country
from o in ords
select new {c.ContactName, o.OrderId, z};
Again, this is quite high on the TODO list.
- Support for custom functions. This is actually fully implemented internally, but I just want to review the API usage before I tell you all how to use it - it probably needs a little cleanup from it's current state, but I don't anticipate any major changes.
I've also got a number of TODOs, but mainly cleanup rather than functional, plus I need to work through the error handling to ensure that any queries that are passed in that the provider can't handle are rejected gracefully rather than just barfing (which is the likely case right now).
The only gotcha that I know of is a query that runs just fine but doesn't necessarily return the correct number of results. Specifically, queries like this:
from user in db.Users
select new
{
user.Name,
RoleName = user.Role.Name
};
Right now, this generates a join to the Role table, so any users that don't have a role are not returned. This differs from Linq to SQL where a left join is generated, giving a null RoleName for any users without a role. I believe the Linq to SQL implementation to be correct - the above query doesn't explicitly have any form of filtering (where clause, join clause etc), so you should get back all the users in the database. This one is top of the list, and hopefully will be fixed soon (it's not hard, I've just run out of time!).
In summary, I'm getting pretty happy with the state of the provider and think that it's now ready for general usage - although there are still some important query forms to support, I think there's sufficient there now to do useful work. If you've got good test coverage, then I'd even be happy for it to go live. If you don't have good coverage, then don't come crying to me :)
Of course, this is all in the trunk, so anyone wanting to play either needs to get the trunk source and build it, or take the much easier option of having Horn do the work. Horn builds the trunk on a daily basis, so look for a package built after around 2300GMT on the 16/12/2009 (the package URL on Horn has the datetime stamp in it, so it's pretty easy to spot).
I'm on holiday for the next couple of weeks, and will only have intermittent internet access. However, ping me either by email or twitter with comments / bugs / suggestions, and I'll do my best to reply. Normal service (whatever that is) will return around Jan 4th.
It was the last SIGIST (special interest group in software testing) run by the BCS last week in London. It was reasonably well attended with the Key Note being from Tom Gilb.
I’ve seen Tom Gilb a few times over the last year and I believe his message is well placed for the testing community. I don’t believe I would ever perform his stakeholder analysis process or return on investment stuff but it’s a sound concept.
Highlight of the day was Steven Ramsay from Linklaters. His talk was honest and insightful and a real struggle against the corporate budget cuts the industry has seen recently. He gave real insight in to the difficulties faced by test managers; struggling to balance quality with reduced costs.
Sasha Gilenson was also quite good but I struggled to see how his new environment configuration tool would solve the many problems we all face. The concept is sound (i.e. knowing whether the live environment is different to the test one) but I don’t see how his software will solve this. I need to read a bit more about this.
Lunch was good as usual. Over lunch I attended a vendor demonstration from Fanfare Software. A tool called iTest. Despite the demo not working and the software not doing what was asked for a couple of examples when it did work it rocked. It looked awesome and gave the tester some really powerful testing approaches. Really good team who knew their stuff too. They had techies and sales people there capable of answering some of the tricky questions asked.
The Share Point was awesome. A guy called Brad Burton from 4 Networking gave a really loud, motivating and hilarious presentation on why networking is so valuable and how we should be thankful for the people we have in our lives over the material stuff we often aim for. Really good presentation. Really good personality.
Martin Gijsen then presented on Advance Key Word Driven Testing which was interesting but I didn’t really get the main message. It seemed to be lost in a series of questions and flow interruptions from the audience. He was apparently excellent in the tutorial though so maybe the message didn’t translate to the main stage.
He was followed by, to be brutally honest, a really poor presentation. I felt really sorry for David Harley because it appeared he had been given the notes on the presentation without seeing the slides in advance. It didn’t help that the notes and the slides didn’t even match. At the end I don’t think anyone knew what he was talking about and he himself seemed totally confused. There were also about 20 slides he didn’t even get to…….
Surprise ending was from Stuart Reid as Clive King couldn’t make it. Stuart did a good talk on agile and how to implement it. Very text book but good still.
Overall it was a good day. I’m going to be speaking at the March one in 2010 alongside my colleague Tom Quinn so if you get the chance to pop along that would be cool.
When developing a .Net application that sends out an email it is sometimes useful to write the emails to a local directory rather than send them to a mail server. This is easily done in the SmtpClient configuration in App.Config. The below configuration section will write .eml files to the directory C:\Email (which you will need to create):
<configuration>
<system.net>
<mailSettings>
<smtp deliveryMethod="SpecifiedPickupDirectory">
<specifiedPickupDirectory pickupDirectoryLocation="C:\Email" />
</smtp>
</mailSettings>
</system.net>
</configuration>
Note: if you are using Outlook 2007 you may not be able to open .eml files without an extra step. The file can be opened from the command prompt with the command
"%ProgramFiles%\Microsoft Office\Office12\OUTLOOK.EXE" /eml "<PathToEmlFile>"
or associate the .eml files with a batch file that contains the text
"%ProgramFiles%\Microsoft Office\Office12\OUTLOOK.EXE" /eml %1
A fuller description can be found at http://support.microsoft.com/kb/956693.
Last night we ran the fourth Southampton Coding Dojo at the iMeta Offices, those present were:
We ran the session again using the Randori Kata approach, with a slight change to previous sessions. As a team we’re into continuous improvement and one of the problems previously encountered was limiting the leading person in the pair to 5 minutes on the keyboard and then passing onto the next person to take the lead – we were finding with this approach that tests could be slow to complete as the first person may only finish part of the implementation and the next person (although meant to be pairing) would undo some of the code and go off in a different direction and get to the end of their 5 minutes and not have a working test either – So in order to avoid this situation this week we tried to improve the process by saying that the first person had to write a failing test, and then the next person had to implement the code to pass the test (using the Red Green Refactor approach) and then write another failing test before passing the keyboard on. This actually worked quite well and we found that roughly each person was still having 5 minutes a go with the keyboard but we had tests that were passing at the end.
The group decided this week that we would implement the Kata Harry, one of the ideas on the coding Dojo website. The problem is based on buying books and giving the customer the biggest discount possible when buying multiple books.
We started off with writing the rules on the white board, which basically followed the pattern:
- There are 5 books and each book costs £8.
- If the customer buys 2 different books they receive a 5% discount (buying 2 of the same results in no discount)
- If the customer buys 3 different books they receive a 10% discount.
- If the customer buys 4 different books they receive a 20% discount.
- If the customer buys 5 different books they receive a 25% discount.
The dojo website also helped by posting all the test cases which was actually a good job because we thought we had completed the task fairly quickly until we ran into the edge cases – The way we implemented the system was by taking the largest unique set we could find and then applying the discount, and then we took the next largest unique set and applied the discount until we had no books left.
However this solution fell down on the edge case of buying 8 books (2 copies of the first book, 2 copies of the second book, 2 copies of the third book, and a single copy of both the fourth and fifth book). The test case indicated the cheapest solution was to create 2 groups of 4 unique books totalling £51.20, however our solution worked by using the largest groups of unique books and hence created a result of £51.60, one group of 5 unique books (£30) and a second group of 3 unique books (£21.60). In order to pass this test we had to rewrite the calculation, we took a brute force approach whereby we tried with all combinations and returned the cheapest – And because we had all the test cases previously working we knew we hadn’t broken existing functionality when they all passed again. I’ll try and post the code soon on the coding Dojo GIT source repository.
We also had some ideas for the next session, including writing a football fixture list calculator, the BBC has an excellent article on how complicated this can actually become with all the dependencies.
In keeping with the 2 week theme and avoiding the NxtGen Southampton user group meeting the next session will be ran on Wednesday 10th December at iMeta’s Southampton Office. If you would like to attend, its all free and we’re an easy to get on with bunch of people, all we ask is that you register first.
We’ve got a VS2010 TFS Beta 2 server going on a VM. Here are some more hints…
- Follow this guide or Google for one:
- Create a DOMAIN user called DEV\TFSReports. The TFS server must also be on a domain.
- An install with no projects takes ~18GB (windows, SQL, WSS, TFS) so make sure you have at least a 30GIG drive.
- Make sure you have Windows 2008 SP2 an all its updates.
- I’m not sure who you should login as while installing. A domain account helps so you don’t keep getting prompted when adding domain users. I used the DEV\TFSReports.
- Make sure the reports user can login locally. http://blogs.msdn.com/ablock/archive/2008/09/18/setting-the-properties-the-log-on-as-a-service-and-allow-log-on-locally.aspx. If you don’t, SharePoint won’t work.
![clip_image002[4] clip_image002[4]](http://blogs.imeta.co.uk/images/blogs_imeta_co_uk/mblake/WindowsLiveWriter/VS2010TFSBeta2_9CC4/clip_image0024_thumb.jpg)
- Don’t add any users to SQL.
- Don’t worry about SharePoint app pool warning when configuring TFS.

- Add DEV\Developers (or people you want to create projects) to the Contributors on the Reporting Service admin site http:\\tfs2010\Reports (Note; the Reports site is not listed in IIS). This fixes the TF30224 error when a user tries to create a new project.

- If SharePoint fails, you can try to re add the extensions from Programs and Features > click Add features (not repair). http://social.msdn.microsoft.com/Forums/en-US/tfsprerelease/thread/e3f80d66-2d8a-46b1-a7da-efb61d86abbc
- You need to add domain users to a few places before creating a project: http://msdn.microsoft.com/en-us/library/dd547204(VS.100).aspx
- I didn’t manage to create the WSS site under site due to SharePoint permission issues. (I’ll update this blog when I figure it out)
- Don’t worry about the object not set (Beta 2?) error when adding a project, just re add the project to VS.

- Make sure you add Task before checking the portal/reports. http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=499387. This will get rid of the rsInvaildReportParameter for the WorkItemTypeParam after 30mins or so.

The scrum stuff looks good but its hard to tell until you use it. http://msdn.microsoft.com/en-us/library/dd997796(VS.100).aspx
Next steps include getting a Build server and a Test Controller going. Lab management requires Hyper-V which Joe Gough is sorting out.
Cool. M
Comments welcome.
We want to be able to control the version of each of our releases. This was being done by leaving the Assembly Info to 1.0.*
Soon it was time to release and we needed greater control of the version number. On a previous project we had a Version.xml file that was stored in source control. This was “Got” on each build, read in, +1’d, updated and checked back in. It also updated a comment (RegEx replacement) in the AssemblyInfo.cs file with the new version number. We also share the Version.cs file by adding it as a link to each project (the dropdown on the Add button when adding an existing file to project)
The drawbacks to this were that you had to prefix the comment when you checked in with “*** NO_CI” and you had to manage the workspaces to get the latest. It was also to set the build number as you had to perform a check in.
This time, there was a check-in policy that prevented checking the version file back in. Instead of solving this (it looked like its was going to be a custom task) we have moved to a version file next to the Drop folder of the build server (on a network share).
Now we can have different version files on the network share and the release manager can update the version numbers as required. The revision number is still incremented by the msbuild scripts. This file is also updated once the build completed with the location of the drop. The last good drop path is used when automatically deploying.
Note we have also had problems with the difference between FileVersion and AssemblyVersion. http://support.microsoft.com/kb/556041
Branching
So that we can support new work and bug fixes, we have used TFS Source control to branch releases.
After you have created a branch, you need to create a new build type. It’s just as quick to compare the previous build type and just update the version number and path. You must then also create a \\projectbuildserver\Public\Versions\Branch_2.1.3.xml file and set the version number up. The project contains information about updating the xml file (note posted).
This means that each branch is under CI with shippable build popping out after each check in.
Note that labels are removed when the builds are removed. So if the retention policy removes them, the label is lost. To stop this, keep all builds (if you have the space) or mark a build as retain indefinitely on the completed builds window.
Once the build is completed, the version.xml file is updated with the location of the build so that a deployment process can always get the latest.
kk. Comments welcome.