Author Archives: steve

A good, lazy way to write tests

Testing. I’ve been thinking a lot about testing recently. As part of code reviews I’ve done for various projects, I’ve seen thousands of lines of untested code. This is not just a case of test coverage statistics pointing this out, it’s more a case of there not being any tests at all in this projects. And the two reason I keep hearing for this sad state of affairs? “We don’t have time”, swiftly followed by “We’ll do it when we’ve finished the code”.

What I present here is not a panacea for testing. It covers unit testing, and specifically, unit testing of interfaces. Interfaces are good things. Interfaces define contracts. Interfaces, regardless of how many implementations they have, can be tested easily and with very little effort. Let’s see how, using this class structure as an example.

A simple inheritence tree

CustomerService is our interface. It has two methods, to keep the example simple, and is described below. Note the javadoc – this is where the contract is described.

public interface CustomerService
{
    /**
     * Retrieve the customer from somewhere.
     * @param userName the userName of the customer
     * @return a non-null Customer instance compliant with the userName
     * @throws CustomerNotFoundException if a customer with the given user name can not be found
     */
    Customer get(String userName) throws CustomerNotFoundException;

    /**
     * Persist the customer.
     * @param customer the customer to persist
     * @return the customer as it now exists in its persisted form
     * @throws DuplicateCustomerException if a customer with the user name already exists
     */
    Customer create(Customer customer) throws DuplicateCustomerException;
}

As we can see from the diagram, we have two implementations of this class, RemoteCustomerService and CachingCustomerService. The implementations of these are not shown, because they don’t matter. How can I say this? Simple – we are testing the contract. We write tests for every method in the interface, along with every permutation of the contract. For example, for get() we need to test what happens when a customer with the given user name is present, and what happens when it isn’t present.

public abstract class CustomerServiceTest
{
    @Test
    public void testCreate()
    {
        CustomerService customerService = getCustomerService();
        Customer customer = customerService.create(new Customer("userNameA"));

        Assert.assertNotNull(customer);
        Assert.assertEquals("userNameA",
                            customer.getUserName());
    }

    @Test(expected = DuplicateCustomerException.class)
    public void testCreate_duplicate()
    {
        CustomerService customerService = getCustomerService();
        Customer customer = new Customer("userNameA");
        customerService.create(customer);
        customerService.create(customer);
    }

    @Test
    public void testGet()
    {
        CustomerService customerService = getCustomerService();
        customerService.create(new Customer("userNameA"));
        Customer customer = customerService.get("userNameA");
        
        Assert.assertNotNull(customer);
        Assert.assertEquals("userNameA",
                            result.getUserName());
    }

    @Test(expected = CustomerNotFoundException.class)
    public void testGet_noUser()
    {
        CustomerService customerService = getCustomerService();
        customerService.get("userNameA");
    }

    public abstract CustomerService getCustomerService();
}

We now have a test for the contract, and at no point have we mentioned any of the implementations. This means two things:

  • We don’t need to duplicate the tests for each and every implementation. This is a Very Good Thing.
  • None of the implementations are being tested. We can correct this by adding one test class per implementation. Since each test class will be almost identical, I’ll just show the test of RemoteCustomerService.
public class RemoteCustomerServiceTest extends CustomerServiceTest
{
    public CustomerService getCustomerService()
    {
        return new RemoteCustomerService();
    }
}

And that’s it! We now have a very simple way to test multiple implementations of any interface by putting in the hard work up front, and reducing the effort of testing new implementations to a single, simple method.

This post is part of a series as explained in I am not a good teacher.

I am not a good teacher

It’s been an interesting few months since I last wrote anything here. Apart from a fairly hectic project in work, my wife is re-training as a software developer and so I’ve been able to find out many ways in which I am deficient as a teacher. The teachers on her course have done a good job in bringing her to the position where she can quite happily code away on server-side code or Android applications, and it’s only when I try to answer one of her questions that I realise how hard it is to teach someone who has some knowledge and not much experience, relatively.

So, I’ve decided to write a series of posts that cover some basic topics but which – crucially – I never see being used out in industry. This is as much a way for me to learn how to talk at the correct level as it is to pass on technical information – hopefully, both parts will be successful. And so to business, with part 1 – A good, lazy way to write tests.

Inferred exceptions in Java

Wow, I can’t believe it’s been 6 weeks since I last blogged. I still need to write up a review on Plumbr, after seeing it in action at Devoxx, but to ease me back into the writing game, here’s a small(-ish) but useful spot of Java.

It’s always nice to borrow and steal concepts and ideas from other languages. Scala’s Option is one idea I really like, so I wrote an implementation in Java. It wraps an object which may or may not be null, and provides some methods to work with in a more kinda-sorta functional way. For example, the isDefined method adds an object-oriented way of checking if a value is null. It is then used in other places, such as the getOrElse method, which basically says “give me what you’re wrapping, or a fallback if it’s null”.

public T getOrElse(T fallback)
{
    return isDefined() ? get() : fallback;
}

In practice, this would replace tradional Java, such as

public void foo()
{
    String s = dao.getValue();
    if (s == null)
    {
        s = "bar";
    }
    System.out.println(s);
}

with the more concise and OO

public void foo()
{
    Option<String> s = dao.getValue();
    System.out.println(s.getOrElse("bar"));
}

However, what if I want to do something other than get a fallback value – say, throw an exception? More to the point, what if I want to throw a specific type of exception – that is, both specific in use and not hard-coded into Option? This requires a spot of cunning, and a splash of type inference.

Because this is Java, we can start with a new factory – ExceptionFactory. This is a basic implementation that only creates exceptions constructed with a message, but you can of course expand the code as required.

public interface ExceptionFactory<E extends Exception>
{
    E create(String message);
}

Notice the <E extends Exception> – this is the key to how this works. Using the factory, we can now add a new method to Option:

public <E extends Exception> T getOrThrow(ExceptionFactory<E> exceptionFactory,
                                          String message) throws E
{
    if (isDefined())
    {
        return get();
    }
    else
    {
        throw exceptionFactory.create(message);
    }
}

Again, notice the throws E – this is inferred from the exception factory.

And that, believe it or not, is 90% of what it takes. The one irritation is the need to have exception factories. If you can stomach this, you’re all set. Let’s define a couple of custom exceptions to see this in action.

public class ExceptionA extends Exception
{
    public ExceptionA(String message)
    {
        super(message);
    }

    public static ExceptionFactory<ExceptionA> factory()
    {
        return new ExceptionFactory<ExceptionA>()
        {
            @Override
            public ExceptionA create(String message)
            {
                return new ExceptionA(message);
            }
        };
    }
}

And the suspiciously similar ExceptionB

public class ExceptionB extends Exception
{
    public ExceptionB(String message)
    {
        super(message);
    }

    public static ExceptionFactory<ExceptionB> factory()
    {
        return new ExceptionFactory<ExceptionB>()
        {
            @Override
            public ExceptionB create(String message)
            {
                return new ExceptionB(message);
            }
        };
    }
}

And finally, throw it all together:

public class GenericExceptionTest
{
    @Test(expected = ExceptionA.class)
    public void exceptionA_throw() throws ExceptionA
    {
        Option.option(null).getOrThrow(ExceptionA.factory(),
                                       "Some message pertinent to the situation");
    }

    @Test
    public void exceptionA_noThrow() throws ExceptionA
    {
        String s = Option.option("foo").getOrThrow(ExceptionA.factory(),
                                                   "Some message pertinent to the situation");
        Assert.assertEquals("foo",
                            s);
    }

    @Test(expected = ExceptionB.class)
    public void exceptionB_throw() throws ExceptionB
    {
        Option.option(null).getOrThrow(ExceptionB.factory(),
                                       "Some message pertinent to the situation");
    }

    @Test
    public void exceptionB_noThrow() throws ExceptionB
    {
        String s = Option.option("foo").getOrThrow(ExceptionB.factory(),
                                                   "Some message pertinent to the situation");
        Assert.assertEquals("foo",
                            s);
    }
}

The important thing to notice, as highlighted in bold above, is the exception declared in the method signature is specific – it’s not a common ancestor (Exception or Throwable). This means you can now use Options in your DAO layer, your service layer, wherever, and throw specific exceptions where and how you need.

Download source: You can get the source code and tests from here – genex

Sidenote
One other interesting thing that came out of writing this was the observation that it’s possible to do this:

public void foo()
{
    throw null;
}

public void bar()
{
    try
    {
        foo();
    }
    catch (NullPointerException e)
    {
        ...
    }
}

It goes without saying that this is not a good idea :)

Devoxx 2012 Swag

Devoxx 2012 has been and gone, and it was a fantastic few days in Antwerpen. I caught up with a lot of great people, made the acquaintance of even more and saw some superb presentations (plus a couple of duds, but there’s an NFC voting system to express this).

I’ve got a lot of material that I need to write up – so much so that I’m getting carpel tunnel syndrome just thinking about it – but in the meantime, here’s a rundown of the things that really count.

This year at Devoxx, some true geeks gave to me

  • 14 assorted t-shirts
  • 12 pens
  • 2 notepads
  • 3 books – Scala in Depth, MongoDB: The Definitive Guide and Infinispan Data Grid Platform
  • 24 badges
  • 2 MongoDB monkeys (or possibly apes)
  • 1 card reader
  • One extremely orange headband
  • 1 frisbee
  • 3 lanyards
  • Numerous chocolate bars and assorted confectionery
  • 1 iPhone cover
  • 3 tins of mints (hopefully not because of my breath)
  • An already distributed and therefore uncountable large stack of stickers
  • Something that may or may not be a gonk
  • 2 yo-yos
  • A block of post-it notes
  • 1 bag
  • 1 hoodie (which I unfortunately lost)
  • 1 pack of agile poker planning cards
  • 1 lens cleaner in a pouch
  • 1 bicycle seat cover
  • 2 three-dimensional puzzles
  • Various refrigerator magnets

Thanks to Ben Verbeken for giving me a lift home after it became clear I couldn’t carry it all home using the train!

Back to Basics – good comments are targeted comments

I can’t think of a single person who enjoys writing comments in code. I don’t, my friends and colleagues don’t, and I’m pretty sure there isn’t a meetup group for fans of it. Outside of code that I write for blog posts, I can pretty much guarantee there are only place where I write comments is in interfaces.

The simple reason for this is that a) interfaces should be explicit, and b) implementations should be self-documenting.

The reason for this blog post is that I’m seeing a lot of code at the moment that falls into one of two traps – either everything is documented, or nothing is documented.

Everything is documented

Spot what’s wrong with this code:

/**
 * The foo class represents blah blah blah...and so on, describing the 
 * class in such detail it's a pity the code couldn't be generated from it
 */
public class Foo {

    /** The name */
    private String name;

   /**
    * Do something with this instance.
    */
   public void doSomething() {
      // Get the name
      String localName = this.getName();

      // munge the local name
      localName.munge();
   }

   /**
    * Get the name.
    * @return the name
    */
   public String getName() {
      // return the name 
      return this.name;
   } 

   /**
    * Set the name.
    * @param name the name
    */
   public void setName(String name) {
      // set the name 
      this.name = name;
   } 
}

Or, to put it another way, spot what’s right with it. That’s a much shorter answer. The code is full of unnecessary comments – e.g. getName() gets the name – and code that seems to have been written just so it could be commented – e.g; String localName – this.getName(); The names have been changed to protect the guilty, but this is real code I’ve seen in a live codebase.

As as I’m concerned, implementations don’t need code-level comments because that’s what the code does anyway.

Nothing is documented

At the other end of the scale is this little gem:

public interface Parser {
    void parse(InputStream is) throws IOException, SQLFeatureNotSupportedException 
}

Interfaces, on the other hand, should have clear documentation that defines what goes in, a generic description of what should happen, and a clear description of what comes out and what exceptions can be thrown. Information at this level should state if, for example, null arguments are allowed, if a null value can be returned, the circumstances in which certain exceptions can be thrown, and so on.

Interfaces should be explicit

Interfaces, to my way of thinking, are contracts, and contracts – as any blood-sucking lawyer can tell you – exist to be honoured. They can’t be honoured if the terms are not explicitly set out.

There are no Burger Kings in Belgium, so if I’m in the UK or the Netherlands I am generally tempted to have one. On my most recent visit, I noticed this at the bottom of the receipt:

Free drink with any adult burger with this receipt Excluding Hamburger, Cheeseburger or King deal or any promotional offers”

Or, to put it another way…

/**
 * Get the free drink promised by the receipt.  This is valid for any burger.
 * @param burger the burger
 * @param receipt the receipt
 * @returns the free drink
 * @throws InvalidBurgerException if the burger doesn't qualify for a free drink
 */
public Drink getFreeDrink(Burger burger,
                          Receipt receipt) throws InvalidBurgerException {
    if (MealType.HAMBURGER == meal.type()
        || MealType.CHEESEBURGER == meal.type()
        || MealType.KING_DEAL == meal.type()
        || meal.isPromo()) {
        throw new InvalidBurgerException();
    }
    return new Drink();
}

To my simple brain, this is confusing and contradictory as hell. Your API should be clear and (as my university teachers beat into me) unambiguous – for example, the words “any” and “except” should not appear in the same sentence. Strive for clarity – if you find your API is too hard to clearly document, there’s a good chance it will be annoying to use. In the case above, an improvement would be something along the lines of

/**
 * Get the free drink promised by the receipt.  Not every burger qualifies for a free drink.
 * @param burger the requested burger.  This may or may not qualify for a free drink
 * @param receipt the receipt containing the offer
 * @returns the free drink. May be null, depending on the burger
 */
public Drink getFreeDrink(Burger burger,
                          Receipt receipt) {
    // implementation
}

Note that I’ve also got rid of the exception as a result of being more explicit.

Conclusion

Annoying as it is, documentation is extremely important in the correct place and completely useless everywhere else. Done correctly, they will make your API easier to use and easier to maintain, which is generally a good thing.

Here endeth the lesson.

Functional tests in Play! 2 with REST-assured

In the Play! documentation, several flavours of functional testing are mentioned, including starting a HTTP server for the duration of the test – this is very useful for testing web services. In code, this looks like

import static play.test.Helpers.*;
...
@Test
public void testInServer() {
  running(testServer(3333), new Callback0() {
      public void invoke() {
         assertThat(
           WS.url("http://localhost:3333").get().get().status
         ).isEqualTo(OK);
      }
  });
}

A few days ago, when I was looking for a good way to test applications built on a CXF stack, I came across REST-assured. It was simple, elegant and useful so it’s the one I picked. In that case, I had to build some extra support for handling Jetty start-up and shutdown, application loading, etc.

With the full-stack approach of Play, however, you get this support for free out of the box, so all that is left is to integrate the tool of your choice. To add REST-assured to your project, edit the project/Build.scala file and add the dependency. If you already have Play running, don’t forget to reload the project for the changes to be visible.

val appDependencies = Seq(
    "com.jayway.restassured" % "rest-assured" % "1.7" % "test"
)

The above example re-written to use REST-assured then looks like this:

import static play.test.Helpers.*;
import com.jayway.restassured.RestAssured;
...
@Test
public void testInServer() {
  running(testServer(3333), new Runnable() {
      public void run() {
         RestAssured.expect().statusCode(200).when().get("http://localhost:3333");
      }
  });
}

REST-assured defaults to localhost for requests, so it’s possible to cut down on the boilerplate and use relative URLs by tweaking the configuration a little.

import static play.test.Helpers.*;
import com.jayway.restassured.RestAssured;
...
private static final int PORT = 3333;

@Before
public void setUp()
{
    RestAssured.port = PORT;
}
...
@Test
public void testInServer() {
  running(testServer(PORT), new Runnable() {
      public void run() {
         RestAssured.expect().statusCode(200).when().get("/");
      }
  });
}

I’ve written a small example that’s available on github, and here’s a sample from it which features an entity being persisted, and then retrieved for testing.

@Test
public void testGetBeer_present()
{
    running(testServer(PORT), new Runnable()
    {
        @Override
        public void run()
        {
            // Create the beer
            RestAssured.given()
                       .contentType(ContentType.JSON)
                       .content("{\"name\":\"Westmalle\"}")
                       .expect()
                       .statusCode(200)
                       .when()
                       .post("/");

            // Retrieve it by name, and bind the response body into a Beer instance
            Beer beer = RestAssured.expect()
                                   .statusCode(200)
                                   .when()
                                   .get("/Westmalle")
                                   .andReturn()
                                   .body()
                                   .as(Beer.class);
            Assert.assertEquals("Westmalle",
                                beer.name);
        }
    });
}

To run the tests, just start up Play in on your command line, and use the test command:

[play-rest-assured] $ test
[info] controllers.BeersTest
[info] + controllers.BeersTest.testGetAll_emptyDatabase
[info] + controllers.BeersTest.testGetAll_populatedDatabase
[info] + controllers.BeersTest.testGetBeer_emptyDatabase
[info] + controllers.BeersTest.testGetBeer_present
[info] + controllers.BeersTest.testUpdate
[info] 
[info] 
[info] Total for test controllers.BeersTest
[info] Finished in 6.954 seconds
[info] 5 tests, 0 failures, 0 errors
[info] Passed: : Total 5, Failed 0, Errors 0, Passed 5, Skipped 0
[success] Total time: 7 s, completed Oct 29, 2012 7:37:52 AM

Resources

Thoughts on the Belgian Play! Framework User Group dojo

The 3rd of October saw the Belgian Play! Framework User Group hold its first coding dojo and overall, it was a success. It’s the first time I’ve been involved in the organisation of anything like this, although the vast majority of the preparation was done by Ben Verbeken.

We had 17 people at the dojo, plus a DBA who stopped by for an hour because he’s interested in how developers and DBAs can work more efficiently together. We had an application specification from an idea by Andy Petrella, along with a reference implementation coded by Ben, Andy and me. We had a meeting room and refreshments provided by Cegeka. In short, we had everything we needed to have a dojo.

Ben has already covered the application in his post on the dojo, so in the spirit of DRY and general laziness I’ll kindly allow you to read about it on his site. Here, I want to cover the bits we got right, the bits we got wrong and how we can correct or improve in the future.

The good

  • The first thing we got right was to actually organise the dojo in the first place. Everything else, good or bad, stems from this.
  • In place of having two people at the front of the room, with a constant rotation of the group’s members, a wireless keyboard was used and passed to the left at the end of each 10 minute time block. This saved time, and meant that the group wasn’t split up into the watching and the watched.
  • Purely by accident, one of the most experienced members of the group (Andy) was sitting pretty much in the middle, and was the first to code after the 10 minute break. The people who coded in the first half of the dojo got an application up and running, and then Andy kicked it up a notch by adding the Twitter4J dependency and coded up to the point of actually posting to Twitter. The effect of seeing the app interact with an external system with just a few minutes’ coding sparked quite a bit of interest and discussion.
  • On the subject of Twitter, we set up a QuickQuizzz application in Twitter ready for use in the dojo. Doing this during the dojo would have been pointless, because it didn’t directly relate to Play! in any way.
  • We ended up with a working application that (pretty much) met the spec! You can find the code on github.

The bad

  • No wifi! Due to a miscommunication, we didn’t get the password for the wifi network and instead had to turn a laptop on the wired network into a wireless access point. The resulting connection was very slow!
  • No choice of IDE! We had IntelliJ set up as the IDE, but it seems that a lot of people preferred Eclipse.
  • The keyboard moved in a linear fashion, always passing to the left. This meant you knew exactly when it was your turn, but also you knew exactly when it was *no longer* your turn. This ties in with the next point, which is…
  • Each person coded only once.
  • The idea for the app was mentioned at the beginning of the dojo, but not written down in a visible place. As a result, we had to clarify the idea throughout the evening.

Improvements for the next dojo

  • Full-speed network access is a must. At least one person didn’t have Play installed at the start of the dojo, and found it impossible to download due to the poor wireless bandwidth.
  • A choice of IDEs – IntelliJ, Eclipse and Netbeans – will be offered, along with a few text editors for people that don’t like or use IDEs.
  • The next person to code should be determined by something other than seating order – maybe numbered cards picked at random, or an app that chooses a name.
  • Because the overhead on switching between coders is reduced by using a wireless keyboard instead of having people physically move, each person should be able to code for e.g. 5 minute time blocks at multiple times during the evening. This would allow everyone to code before the break, at which point the random ordering can start again.
  • The spec should be written up as a very simple bullet list. A second list, with optional functionality, could be introduced if the app has been finished and there is still time left.

And next…?

The rest of the year looks to be pretty busy, with Devoxx coming up in November, plus day jobs, everyone seems to be doing the Coursera Scala course, etc, and then it’s the holiday season again. I think we’ll hold another event early in 2013 – not necessarily a dojo this time, because we have some other ideas to keep it interesting. In the meantime, Play BE will be having another meet-up in Brussels one evening – bring your friends, bring your laptop, bring your ideas and join us there! Details will be announced on http://play-be.org soon.

Twitter4J – quick fix for 401 issues

The Belgian Play! Framework User Group is holding its first dojo on Wednesday, and I’m helping to implement the reference application.  We’re using Twitter as a communication point, and one of the best (the best?) Twitter API for Java is twitter4j.  We have an app set up with Twitter, and everything should have been working fine…except we couldn’t post anything.  Which blows, because that’s kind of the point.

The exception thrown every time we attempted to post was a 401 unauthorised exception.  I double-checked the access rights, and it appeared we had granted read-only permission for the app.  I renewedthe access rights with read-write permissions, and it still didn’t work.  Nuts.

The exact error, for reference, was

twitter4j.TwitterException: 401:Authentication credentials (https://dev.twitter.com/docs/auth) were missing or incorrect. Ensure that you have set valid consumer key/secret, access token/secret, and the system clock is in sync.
{"errors":[{"message":"Could not authenticate you","code":32}]}

The problem lay not with the various keys used to authenticate with Twitter, but with the time on my laptop.  Apparently it was out of sync with the Twitter servers, and this was causing the problem – presumably it’s security against some kind of replay attack.  The solution was extremely simple – I installed the ntp daemon to constantly correct my system time.  The instructions for Ubuntu can be found here. To save you a click, the command you need is

sudo apt-get install ntp

I tried posting again as soon as the daemon was installed, and it worked perfectly.

PS If you’re reading this in the dojo, you owe me a beer :)

Harry Potter and the Personal Lesson

Geek alert.

I love the Harry Potter books. I thought they were well-written and cohesive.  You may have read them too, and felt something similar.  What you may not have done – and you can skip to the end otherwise – is read them back-to-back in a short timespan.

I’ve read all the books multiple times, and every time I’ve picked up something new.  That’s a win, by any reasonable standard.  After book seven came out and I read it, I decided to read all the books in a single extended session.  The results were, frankly, remarkable.  Plot lines and plot points made more sense and there was a complete feeling of a story that required seven books to cover as opposed to seven related books.  The difference, when you’ve experienced it, is significant.

This is the point where I attempt to weave a personal life experience into general software engineering.  Since this was a click event in my life, hopefully I can expand on it thoughtfully in such a way that allows me to cash in on it mercilessly.

A scenario:

Point the first: We have an idea and sketch out functionality and an API

Point the second: We expand that idea, and the API, in accordance with the improvements of point one.

Generally, in my experience, the next bit is where things really go to shit.

Point the third: We add more functionality, refactor the API completely (and rationally, according to the changes) and add things which we think are needed instead of what is wanted.  We put perceived wants in place of planned needs.

And right there, things go wrong.  If you have an idea, then not evolving it is a mistake.  If you have evolved it, then pause a while.  Let it stabilise. Change is good, but if you want to build a following – particularly one which requires a stable API – change must proceed according to what you want to achieve.  Once you’re stabilised and are ready to expand your capabilities again, notify your consumers in ample time and go for it.

If you’re familiar with the the Potter books, you may see a similarity here.  For example, book five got lambasted just because the principle characters were acting as they should have been at the age they were.  Which is ridiculous, because otherwise they wouldn’t have been acting realistically. Just because they’re teenage witches and wizards doesn’t mean they’re not teenagers – they’re just teenagers who are witches and wizards.  Comme çi, comme ça.

The point is, if you’re still reading at this point, is that J. K. Rowling clearly has a view on where she was going, and didn’t fuck up her API.  She wrote according to her plan, and took the criticism as it came.  The result speaks for itself.

If you want to create something, that’s amazing.  If you want it to be useful, you have to listen to your users.  But if you only listen to your users and forget what you wanted to do in the first place, then you’re designing by committee – and that’s guaranteed to give you the worst set of compromises you can imagine.  If you’re going to build something, build it with an eye to making it useful – but keep your vision for it true.

Software people continually try to apply their practices to the general world, but it works both ways.  In this case, not compromising worked for J. K. Rowling, and it’s a damn good lesson.

Shit or get off the pot

I bought my first Playstation 3 about five years ago, and it was great.  I’m not particularly into games, but it was purchased as a media player and it worked perfectly.  Three years later, the dreaded red light of death occurred, and it went into the attic (just in case…).  I was quite impressed that it had a lifetime of three years, given that everything else I bought from Sony generally developed a fault two weeks after the warranty expired.

I mourned its loss – it could play PS1 and PS2 games as well as fulfilling my music and Blu-Ray needs.

But, it’s not like it was the only chance I ever had to buy a Playstation 3.  I bought a new one a couple of months later – only to be seriously, seriously pissed off.  It didn’t play PS1 or PS2 games – black mark number one.  More importantly, it was unable to play Blu-Ray movies without a horrible stutter in the playback.  It took me a few months to take it back – a combination of not enough time and general laziness – but it finally happened about two months ago.  Four weeks ago, I received a replacement PS3 that should have no problem with Blu-Rays.

I call bullshit.

Right now, Harry Potter and the Half-Blood Prince is stuttering its way across the screen.  Sony championed Blu-Ray, yet they seem singularly incapable of producing a machine capable of playing them. I can cope with incompetance when it doesn’t affect me, but when it does it tends to bite a little.  Happily, the good folks at Sony provided a new warranty starting from when I received the new machine.  Unhappily, I’m not sure that returning it to have the problem addressed will have any effect since they have already shown they’re unable to do what they claim to be able to.

I personally wouldn’t sign off any software that I had written or was reviewing that acted as badly as this.  The unbelievably shitty performance means that DVDs (which the PS3 has no problem with) are more watchable on this machine than the hi-def films I bought the machine for in the first place.

Sony – please – shit or get off the Blu-Ray pot.  My patience is running out, and there are a lot of alternatives out there.  Don’t make the mistake of thinking you’re the only player in the market.