Software and People

I'm a great fan of unit testing, including test-driven development and the practices of Extreme Programming (XP). I try and develop unit tests for all the code I write.

Sometimes, of course, unit testing everything is simply not possible. On a project I'm working on at the moment I don't have access to the machines the final system will be running on, and only a sketchy idea of the input data my code will actually recieve. Sure, I have got a test suite to test that all my code does what I think it should do, but there are inevitably misunderstandings and hiccups when it runs on the real system.

Despite all this, the fault that irritates me most is one that I should, and could, easily have caught in my own testing. The doubly irritating part is that I got part way to detecting the fault before I delivered it, but then just got lazy.

In the example data are some integer timestamps, for example 1077797400. I did a little experiment:

Date timestamp = new Date(1077797400);
System.out.println(timestamp);

The system has only been in existence for a few years, so when it printed out Tue Jan 13 12:23:17 GMT 1970 it seemed somewhat unlikely.

I got in touch with my client contact, and he told me that timestamps are stored in seconds, not milliseconds (as used by java.util.Date). "That's easy to fix, I thought. "What can go wrong with simply multiplying by 1000?".

If you are really sharp (or have faced this yourself recently), you should be able to guess the problem, already. Unfortunately, I didn't until I got client complaints about daft dates. My first assumption was that I had simply delivered the wrong code, but when I looked at it, there was my multiplication:

public static Date makeDate(int i)
{
  Date ret = new Date(i * 1000);
  return ret;
}

So I went back to my test code and added the multiplication, like I should have done in the first place:

Date timestamp = new Date(1077797400 * 1000);
System.out.println(timestamp);

It printed out Mon Dec 29 06:30:08 GMT 1969. Multiplying by 1000 had made the same timestamp go back in time

I sure was baffled at the time, but something occurred to me in the middle of the night. First thing when I woke in the morning (well, after having a coffee, eating some breakfast, checking my mail and so on - I'm not that much of a geek) I added a single character to my test code, and got out the much more reasonable Thu Feb 26 12:10:00 GMT 2004.

Figured it out yet?

I added an 'L':

Date timestamp = new Date(1077797400 * 1000L);
System.out.println(timestamp);

This forced the calculation into long arithmetic rather than sticking with integers, and allowed the result to increase rather than overflowing and "wrapping round" into a negative number. Once I realized, the answer was obvious and simple.

So, next time, when someone asks that question about "how do I know what I should test?", I'll remember this. Ten seconds of testing would have saved me the embarassment of client complaints after delivery and a restless night. Sounds like a good deal to me!


Nice story :)
You should link this to another title "A cautionary tale of incomplete specifications". I've encountered something similar in the past. We were working on a major application to make it compatible with a new input format (this application received data from external sources, and the customer was adding new sources using a different file format to their network). We had gotten a thick document detailing every single field and its meaning, all seemed well. Our software was written, tested by ourselves (using data generated by a program written to those same specs of course), tested by the customer (using another program to create data according to those specs), accepted and placed into production. A few days later the first of the new files started arriving and were all promptly rejected by our software as being corrupted. Customer was (of course) extremely annoyed, as these files contained data about billable services they provide. They thought they were going to loose a LOT of income here. On investigating the files (first we went through our own software again AND through our testdata and found them all in accordance with the specs) we quickly discovered the culprit. The life data was NOT in accordance with the specifications supplied by the manufacturer of the machines that generated it. The specs listed a binary 0 as a record terminator, the life data used a text '0' instead. The problem was quickly resolved by allowing both text and binary zeros as record terminators and notifying the manufacturer of the bug in their firmware. One case where exhaustive testing on our end just didn't matter...
TrackBack to http://radio.javaranch.com/frank/addTrackBack.action?entry=1079276361000