The slacker got tagged

Well I've been really slack of late re: the blog and the coding community in general - but at any rate, what brought me back into focus was actually getting email regarding Splicer - yes, there are people using it... who knew?

At any rate, I also noticed that reading back through other peoples blogs - it appears I was actually tagged by Alex James - now I had a personal blog for a while before a technical one, and it's that kind of bollix that drove me away from it as a past time :) but then I'm just a sour sod, soooo.... I've decided to respond, if for no other reason but to confirm that I do in fact read Alex James blog - but I wont bother inflicting the pain on anyone else (plus most of the NZ bloggers have already been swatted a few weeks ago)

Without further delay, 5 things you probably don't know about me:

1) I got engaged last year on the rocks at pink beach, omaha.

2) The first programming language I learned was basic for the vic20, followed shortly by gwbasic.  And then (turbo) C++ when I was 11 or 12 (I forget exactly).

3) I bought my first car at the end of last year, a BMW 318ti.

4) I grew up on a farm and was home schooled for a couple of years, got Dux at the dubious Rodney college in Wellsford, and then did some tertiary studies (ie. made new friends) at Unitec.

5) My cat is named shodan, and my previous cat was run over and consequently body-snatched by gypsies or possibly itallians (according to the neighbours at the time...)

Wasn't that fun, tune in next time when I actually post something of value :)

Read More

Apologies...

Apologies for the extended blog down-time... moved house last weekend (and did a few major changes to my network, including a entirely new domain etc.) and only just got a chance to set up an Ubuntu/apache/mod_proxy box for forwarding requests onto the server where my blog lives.

Drop me a comment if anything seems a little wonky (ie. missing images etc.) as I did the config in a hurry  :)

Read More

Odd designer behaviour...

The evil designer attribute ;o)

I'm working on a project at the moment where the client want's an
"Add-in" for an existing piece of software... it's a COM interop
project, .Net 2.0 (and yes, it uses Castle, IoC sits in
nicely with
the IServiceProvider :) and it
hosts a custom designer, and the add-ins must exist in their own
directory, outside of the host applications base directory.

At any rate, it's been working just fine so far, but today I was
trying to introduce some custom designers... and they weren't
being constructed... which highlighted some odd behaviour in the
design time support - I have 6 or so assemblies, most
are support assemblies, some containing controls and their
associated designers, something like this:

public class MyButtonDesigner :
ControlDesigner { ..}

[Designer(typeof(MyButtonDesigner))]

public class MyCustomButton :
ButtonBase { .. }

And then there is the core assembly which hosts the design
surface etc. and contains a number of  COM-visible classes
- one of these class in the core assembly is
constructed via Com interop by it's Name, and everything starts
it's life from there in their Add-in, much like a Main class in a
WinForms project.

So the core assembly references the support assemblies, and has
no problem creating controls at runtime within these
assemblies...

But then...

However, the design time support fails to construct or make use
of the custom designers... and doesn't blow up, it just
fails silently - personally I hate this kind of behaviour...
I like things to fail fast, in your face :)

It appears the custom designer is never constructed because
the design time infrastructure fails to locate the
assembly it's held within, even though my code has
just created an instance of a control from the same said
assembly not an instant before... arghh!

To my mind this is madness... I'm not passing a string reference
to the type, and the assembly is already loaded, it should be as
simple as using the Type in the attribute to create an instance
of the designer - looking at the internal implementation of
this attribute, I can see where it all goes terribly wrong:

public DesignerAttribute(Type
designerType)

{

      this.designerTypeName =
designerType.AssemblyQualifiedName;

      this.designerBaseTypeName =
typeof(IDesigner).FullName;

}

It's still a puzzle as to why this isn't being resolved... the
assembly is already loaded against the current AppDomain - I
guess I could mess with the fusion logger to figure it out, there
must be some subtle different - but I didn't have the time to
waste, so I just cheated, implementing an
AppDomain.AssemblyResolve event handler to take
care of returning the assembly that's already loaded when
matching against a set of pre-loaded assemblies.

I think the silent failure is what annoys me the most, this is
something which could be easily introduced and might not be
imediately detected when performing a minor update... I'm still
not sure how I could write a test to detect it...

Speaking of custom designers... I'm unimpressed by how most
custom designers are marked internal... especially for classes
that are designed to be subclassed... why the hell is the
ButtonBaseDesigner marked internal?

Read More

New stuff

Vista


Well I've been playing with a couple of things this weekend... first off is Vista... it's been on my laptop for a while now, but I haven't really been using it because It's a bit of a drag getting my development environment set up... but I've been working on that tonight, and have my current project being built successfully... so that's cool... the only real problem is my laptop is no longer a laptop since I've installed Vista...

The problem is power... where as I could squeeze upwards of 2 to 2.5 hours from the battery under xp, in power saving mode with Vista I get an hour if I'm lucky... it says exactly 1 hour of available capacity after boot, every "build all" in Visual Studio reduces that number by about 5 minutes... this is not particularly useful, heading to a cafe to code for an hour or two is looking problematic - At first I thought that perhaps the minimum speed step value wasn't being utilized, but according to the resource monitor it's at "37%" of the maximum frequency, which is ~800mhz, which seems right... So I have no idea why my battery life is so bad.

Second to this, max performance is only workable for about two hours before the machine will freeze - too much heat - not a new problem, certainly this was an issue under xp sp2 or win2k3 - Incidentally it's an HP NW8240 which scores 4 on the "Vista" performance scale (Pentium M 2.13ghz, 2gig ram, ATI FireGL V5000) and the cooling solution is really not adequate to deal with the heat generated from the processor and GPU at max performance, but it only cropped up while gaming in the past with XP SP2... now this happens while working with Visual Studio 2005 for a couple of hours... I suspect it's probably because I'm using Aero, the laptops definitely hotter then it used to be, I'll switch off Aero tomorrow and see how I fare, problem goes away if I switch to balanced performance mode of course, but without max performance Resharper 2.02 is an utter dog.

Other problem I have is getting bluetooth to work, which is a bit of a pain when using my phone as I've lost the mini-SD adaptor so I can't slot it into my monitors card reader... more a minor niggle though, I can live without it for now.

Supreme Commander


The other thing I've been playing with this weekend was the Supreme Commander beta, spiritual successor to Total Annihilation... TA was the defacto RTS we played while I was at uni, and it also served as the standard by which we measured any future RTS's (and  generally found them wanting, especially when it came to an interface for selecting, scheduling and command units)...

SupCom is very faithful to the concepts and feel of TA, and it runs just fine on Vista, but I did end up lowering all the settings, and still suffered from overheating and crashes after half an hour of play on my laptop... *sigh* I need to experiment a little further to see how the dual screen support works, but so far the game looks great, and it provides some wonderful strategic views which make it easier to control masses of units (And I do mean masses, hopefully the multi-threading support is good, I could definitely see this game benefiting from dual-core, or maybe quad-core once an implementation is released which isn't quite so gimped)... looking forward to getting my hands on the finished product when it's released next year.

Read More

More Base4 mocking...

More mocking...

The title for this entry should probably be "more IRepository mocking" - as that's what I'm mocking out... but at any rate, let's get down and dirty... In this entry I will cover mocking out a "complex" method, incidentally I think the size of this test suggests that our test isn't fine grained enough (and that individual features within the data loader should be exposed so we can test them better) - but that's my problem, not yours ;o)

Background

So, as some background, this is a test for a class called "DataLoader" - a most unimaginative name for a class that loads data... it's used for loading an XML file which has a bunch of info for different entities... if your recall the last entry, I was talking about simplistic music information, well this class can be used to load musical data into the store, including something not mentioned so far, which is the "test suite" - the test suite defines tests which can be run against the music in the store, the details aren't really up for discussion, but it's for executing customer acceptance tests, not unit tests.

The test below checks one of the primary paths, where by no pre-existing data is available, so it must all be created by the data loader first, before creating the customer acceptance test, which references the track, release etc. data (the definition for customer acceptance test suites are also stored in the store, as are the results from run the customer acceptance tests).

Diving In...

So here is the test for loading a "test suite" for a single track, the song "Dirty Harry" by the "Gorillaz", we are testing the behavior of loading the file when the track, release and artist do not already exist in the store - most of this is basic RhinoMock's usage (though I'm no expert, I'm probably misusing dome of the features...).

[Test]
public void LoadDataFreshForSingleTrack()
{
/**** RECORD ****/

Environment environment = new Environment();
SourceDevice sourceDevice = new SourceDevice();
RecordingDevice recordingDevice = new RecordingDevice();
Track track = new Track();

ObjectPath artistPath = (Artist.Fields.Name == "Gorillaz");
Expect.Call(_artistRepository.FindOne(artistPath)).Constraints(Base4Query.PathEqual(artistPath)).Return(null);

_artistRepository.Save(null);
LastCall.Constraints(Property.Value("Name", "Gorillaz"));

ObjectPath releasePath = (Release.Fields.Name == "Demon Days");
Expect.Call(_releaseRepository.FindOne(releasePath)).Constraints(Base4Query.PathEqual(releasePath)).Return(null);

_releaseRepository.Save(null);
LastCall.Constraints(Property.Value("Name", "Demon Days") &&
Property.ValueConstraint("Artist", Property.Value("Name", "Gorillaz")));

_trackRepository.Save(null);
LastCall.Constraints(Property.Value("Name", "Dirty Harry") &&
Property.ValueConstraint("Release", Property.Value("Name", "Demon Days")));

ObjectPath encodingPath = (TrackContentEncoding.Fields.Name == "MP3");
Expect.Call(_encodingRepository.FindOne(encodingPath)).Constraints(Base4Query.PathEqual(encodingPath)).
Return(null);

_trackContentRepository.Save(null);
LastCall.Constraints(Property.ValueConstraint("Track", Property.Value("Name", "Dirty Harry")));

ObjectPath trackReferencePath = (Track.Fields.Name == "Dirty Harry" &&
Track.Fields.Release.Name == "Demon Days" &&
Track.Fields.Release.Artist.Name == "Gorillaz");

Expect.Call(_trackRepository.FindOne(trackReferencePath)).Constraints(
Base4Query.PathEqual(trackReferencePath)).Return(track);

Expect.Call(_trackRepository.FindOne(trackReferencePath)).Constraints(
Base4Query.PathEqual(trackReferencePath)).Return(track);

ObjectPath environmentPath = (Environment.Fields.Name == "Indoors");
Expect.Call(_environmentRepository.FindOne(environmentPath)).Constraints(
Base4Query.PathEqual(environmentPath)).Return(environment);

ObjectPath sourceDevicePath = (SourceDevice.Fields.Name == "Loopback");
Expect.Call(_sourceDeviceRepository.FindOne(sourceDevicePath)).Constraints(
Base4Query.PathEqual(sourceDevicePath)).Return(sourceDevice);

ObjectPath recordingDevicePath = (SourceDevice.Fields.Name == "Loopback");
Expect.Call(_recordingDeviceRepository.FindOne(recordingDevicePath)).Constraints(
Base4Query.PathEqual(recordingDevicePath)).Return(recordingDevice);

_testSuiteRepository.Save(null);
LastCall.Constraints(Property.Value("Name", "One track, one loopback sample") &&
Property.Value("Description",
"Testing with 30 second snippets pulled from the source files with audacity (Loopback)") &&
Property.ValueConstraint("Tracks",
ItemListOf.NumberOfItems(1) && ItemListOf.IndexedItemConstraint(0, Is.Same(track))));

/***** REPLAY *****/

_mockRepository.ReplayAll();

DataLoader loader = CreateLoader();

loader.LoadData(OneTrackFile, ContentPath);

_mockRepository.VerifyAll();
}

First you'll probably see that the callback and anonymous delegates from the last post are absent, they've been replaced with constraints... which has made the syntax a lot more concise - so far the rhino mocks support is pretty basic, we have two static classes being used for creating the constraints:

  • Base4Query - for constraints on ObjectQuery and ObjectPath arguments/properties.
  • ItemListOf - for constraints on IItemList arguments/properties.

The constraints method in RhinoMocks expects the same number of arguments as the associated method call on the mock object, constraints work on the arguments value, however you can apply constraints to the value of single property of an argument, and those constraints can be logically AND or OR'd together - for instance here:

_testSuiteRepository.Save(null);
LastCall.Constraints(Property.Value("Name", "One track, one loopback sample") &&
Property.Value("Description",
"Testing with 30 second snippets pulled from the source files with audacity (Loopback)") &&
Property.ValueConstraint("Tracks",
ItemListOf.NumberOfItems(1) && ItemListOf.IndexedItemConstraint(0, Is.Same(track))));

We are applying these constraints:

  • The first argument should:
    • Have a property called "Name", and it's value should be "One track, one loopback sample".
    • And have a property called "Description", and it's value should be "Testing...." etc.
    • And have a property called "Tracks", which is of type IItemList which:
      • Contains a total of 1 items
      • And the item at index 0 should:
        • Be the same as the instance returned from an earlier save call.







Notice that we have to call the method we want to apply constraints to first, so that we can address it with LastCall - a necessary evil when the method has a return type of void... otherwise you can use the more pleasant "Expect.Call(...)" convention.

Also, I'm declaring instances of the expected Base4 object paths, and then applying them in the constraints... I could in-line the object paths, but I then need to disambiguate the overloaded call to the FindOne method, so we have my preference:

ObjectPath trackReferencePath = (Track.Fields.Name == "Dirty Harry" &&
Track.Fields.Release.Name == "Demon Days" &&
Track.Fields.Release.Artist.Name == "Gorillaz");

Expect.Call(_trackRepository.FindOne(trackReferencePath)).Constraints(
Base4Query.PathEqual(trackReferencePath)).Return(track);

or this:

Expect.Call(_trackRepository.FindOne((ObjectPath)null)).Constraints(
Base4Query.PathEqual(Track.Fields.Name == "Dirty Harry" &&
Track.Fields.Release.Name == "Demon Days" &&
Track.Fields.Release.Artist.Name == "Gorillaz")).Return(track);

The second involves less code, but I think it's easy to get confused between the compile time query language's logical operators and those of the RhinoMock's constraints... each have merits of course.

Conclusion...

I'm still working through some of the finer points of the implementation (and so won't publish any code just yet...) but this should give you some ideas about how you can test base4 interactions in your application with RhinoMocks, instead of hitting the base4 server and testing the results... and I think it's a lot more intuitive when attempting to do TDD with base4...

I would also like to make a special mention that this code won't actually work with the latest published release of Base4... there is an issue preventing the addition of items to an IItemList in some situations when you have not set a valid default context - there is a fix (Which works well) in the Base4 trunk, and I assume Alex James will release this at some point... and thanks for resolving that issue so quickly Alex :) much appreciated.

Read More