Archive for May, 2008

h1

Time is of the essence

May 29, 2008

After last week’s game, it is now seven straight games since I’ve lost. White, I think, was slightly better through most of the game. Near the end of the game, even when I went up a pawn, he had plenty of chances to force a draw. Instead he failed to find the right moves in time trouble. I normally have a very strong instinct to play quickly in my opponent’s time pressure, not giving him a chance to think on my time. This time, I took efforts not to make that mistake – instead, I tried to choose continuations which would force him to calculate multiple variations at critical junctions. End result? I won on time. The game, as always, can be replayed here.

1. d4 d5 2. c4 c6 3. e3 Nf6 4. Nc3 e6 5.Nf3 Nbd7 6. Bd3 dxc4 7. Bxc4 b5 8. Bd3 Bd6 9. Qc2 Bb7

I have now acquired a copy of Fritz 11 and this position has occurred twice in its database. After 10.O-O O-O (which was played in the above games), it transposes into more common variations.

10. Ne4 Bc7 [10…Nxe4 11.Bxe4 Rc8] 11. Bd2 O-O 12. O-O Rc8 13. Nc5

I was fully expecting Bb4 at this point. I didn’t like Nc5 as after I capture the white knight, both recaptures help Black’s position. If Qxc5, Bb6 and I can start thinking of moves like c5, when I challenge white’s control of the center and my pieces start coming to life. If dxc5, as in the game, white loses his pressure on the c-file, he has no direct threats and I can start repositioning my pieces to more active squares.

13…Nxc5 14. dxc5 e5

The computer prefers Qd5 or a5. In fact I considered both of these moves but couldn’t come up with a clear plan. With e5, my idea was this. First of all, if white made some random move, I was hoping to play Re8, followed by e4, and I am clearly better. On the other hand, if white played e4 himself, he closes the light-squared diagonal, which is where he exerts the most pressure. By the same token, my dark-squared bishop is now useless as well, but with a closed position, I would have enough time to relocate my useless c8 rook and b7 bishop to more useful squares.

15. e4 a5 16. a3 Qe7 17. b4 Ra8 18. Bg5 Bc8 19. Qc3 Re8 20. h3 h6 21. Bc1

My opponent spent a very long time before playing Bc1. I was actually quite surprised; I felt he would play Bh4.

21…Nd7 22. Bc2

Getting out of potential traps like f5, exf5, followed by e4, forking the knight and bishop.

22…Nf8 23. a4 axb4 24. Qxb4 Nd7 25.Be3 Bb6 26. axb5 Rxa1 27. Rxa1 cxb5 28. Qxb5 Bxc5 29. Bxc5 Nxc5 30. Ra5 Nd7+-

Somewhere in my calculations, I had assumed that I would be up a queenside pawn at the end of these tactics. The realization hit home only after White’s 26th move.

31.Ba4 Qa3

By this time, my opponent was in serious time pressure. Being in a worse position, I wanted to make sure I didn’t make too many moves where his response was forced.

32. Qb3 Qc1+ 33. Kh2 Qf4+ 34. g3 Qxe4 35. Qd1 Qf5

With this, we both made the time-control – my opponent with just a few seconds left. With the time-control for the game only adding another 15 minutes for the rest of the game, he wasn’t quite out of the woods yet!

36. Qd5?

As so often happens, he blunders right after making the time-control. Both Ra7 and Ra8 probably lead to forced draws.

36…Nf6 37. Qd1 Qxh3+ 38. Kg1 Rf8?

I gave a good, long think about 38…e4, giving up the exchange but probably winning. But I was unable to calculate all the variations and decided to play it safe.

39. Rxe5 Bb7 40. Qd3 Rc8 41. Re1 Ne4 42. Bd7! Rd8 43. Bxh3 Rxd3 44. Bg2 Nf6 45. Ne5 Rb3 46. Bxb7 Rxb7 0-1

The position is probably quite drawish at this point, but my opponent, having burnt most of his remaining time on his 37th move finally ran out of time and I won my second game of the year on a time forfeit!

h1

Mock me not

May 20, 2008

This is a follow-up to an earlier post of mine, where I ranted about the (ab)use of mocks in unit tests. Last week, I got into a debate with one of my colleagues on the same issue. He was of the opinion that mocks were THE way to go in unit tests. You just don’t test with real stuff at unit test level. You mock out all your dependencies and only test your code.

I agree. In principle. But like all rules, there are some exceptions. Lets take an over-simplified example. Suppose we are writing the get() method in a DAO class, in test-first fashion. The test might initially look like this:

 @Test
 public void testTest() throws Exception {
  // setup
  Statement statement = EasyMock.createMock(Statement.class);
  EasyMock.expect(
    statement.executeQuery(EasyMock.isA(String.class))).
    andReturn(EasyMock.createMock(ResultSet.class));
  EasyMock.replay(statement);
  RTest test = new RTest();
  test.setStatement(statement);
  
  // act
  Object result = test.get("id");
  
  // assert
  EasyMock.verify(statement);
  assertNotNull(result);
 }

The simplest code to pass the test:

public ResultSet get(String string) {
  try {
   return statement.executeQuery("");
  } catch (SQLException e) {
   e.printStackTrace();
   return null;
  }
}

Now, we enhance the test and add more stringent assertions:

 @Test
 public void testTest() throws Exception {
  // setup
  String expectedSql = "select * from table where id='id'";
  Statement statement = EasyMock.createMock(Statement.class);
  EasyMock.expect(
    statement.executeQuery(expectedSql)).
    andReturn(EasyMock.createMock(ResultSet.class));
  EasyMock.replay(statement);
  RTest test = new RTest();
  test.setStatement(statement);
  
  // act
  Object result = test.get("id");
  
  // assert
  EasyMock.verify(statement);
  assertNotNull(result);
 }

Now, this code fails the test:

public ResultSet get(String string) {
  try {
   return statement.executeQuery(String.format(
     "select * from table where id = '%s'", string));
  } catch (SQLException e) {
   e.printStackTrace();
   return null;
  }
}

Some of the problems with this test:

  • Since the SQL query is part of the code, your test should assert on the correctness of the query itself. By typing it twice (once in the test, once in the code), how exactly do you achieve that goal?
  • Each unit test should test a single unit of code (in this case, a method). Assert on the expected result (of course!), but the test shouldn’t define the implementation. The second run of the test should not fail.
  • An integration test or acceptance test might show any possible problems with the query. But then again, those tests may not cover all boundary cases. The unit test is the closest place to the execution of that piece of code and is the right place to carry out that real test.

How would you do that? Simple. Use an in-memory database, run the CREATE script as part of the test setup (wow, this tests your create script as well – what a bonus!), populate data required for your test, execute your test method, and assert on the data. If you are testing get() functionality, assert on the returned data. If testing save() functionality, assert on the new data in the database. Your unit tests actually test your code (and SQL) without definining how the code should be implemented.

This rationale has been used (extensively) in Aloha’s unit tests. As you might expect, we use Hypersonic to unit-test the DAO classes. Similarly, we use SipUnit to test code which sends and receives real SIP. It provides several benefits to the use of mocks to test SIP:

  • SIP is an incredibly complex protocol. There is no easy way to validate that a newly created SIP message is valid by itself, or valid in the current message flow. If JAIN-SIP sends out the message with no exceptions and it is duly received by SipUnit, we can rest assured that the message is valid.
  • Mocking out SIP responses also results in unmanagable tests. Since the required data in a JAIN-SIP response object is several levels deep, constructing the mocks is quite complicated. If any code is refactored later, the changes to the tests are very time-consuming, without providing the necessary confidence that the changes to the code are accurate. (A by-product of the test defining the implementation)
  • Because SIP is truly real-time, it is essential to test various race-conditions and concurrency issues. While our robustness and performance builds go a long way to achieving this goal, our unit tests provide our first level of sanity checking. With the current unit test setup, it is much easier to write failing unit tests when fixing a concurrency issue than it would with mocks!

This is not to say that we don’t use mocks in Aloha. Infact, they are used extensively across the codebase, just not in ALL places.

h1

What is Aloha?

May 9, 2008

Is it:

  1. Hawaiian, for hello and goodbye?
  2. The Web21C team shirt, represented here by Eastmad?
  3. The newly open-sourced SIP Application Server?

The problem with most SIP Application Servers is that they are built on the SIP Servlet specification. This usually leads to unwieldy implementations that are over-engineered and inflexible. Our goal with Aloha was to create something simple, easy to scale and can easily be embedded into our different voice services. The API we expose to applications is purposefully quite coarse, hiding the horrible workings of SIP and media negotiation from them. (Of course, none of this is really “hidden” – you can always get your hands dirty now that this is open-sourced!)

Dependency Injection has become a very common pattern in writing testable code. Spring Beans lend themselves to this pattern naturally, whereby you can inject properties into beans via constructors and/or setters. We therefore decided to use singleton Spring beans for most of our core classes. As long as you are thread-safe while storing and accessing data, the beans are just an implementation of a finite state machine. You have the added bonus of being able to run the application server within any Java container, as long as you can load your Spring application context!

The beans themselves are layered at different layers of abstraction. The “dialog” layer is at the lowest level of the stack, responsible for sending and receiving SIP messages. Beneath the covers, it uses the open-source JAIN-SIP stack to handle a lot of the internal workings of SIP (such as the resending of messages, handling of retransmissions, working out of timeouts etc). The “callLeg” layer is a thin layer above dialog, meant to abstract most of the SIPpy stuff from the higher levels. Unfortunately, there is still some level of leakiness present at this layer as evidenced by the following declaration in the CallLegBean interface:

void reinviteCallLeg(String callLegId, MediaDescription offerMediaDescription, AutoTerminateAction autoTerminate, String applicationData);

It is a clear violation of the intended abstraction to expose details of media negotiation above the callLeg layer. It has been a story on our backlog for quite a while now to fix this but has consistently moved down the prioritization list!

Moving on, the next layer of abstraction is the “call” layer. This is where the interesting stuff starts (in terms of telephony, it isn’t very interesting if you are on a call by yourself. The most basic call would be to connect two people on a single call). Most applications using Aloha will use the API exposed by CallBean. In addition to calls, there are “media” level beans (both for call legs and for calls), which allow the application to create calls using the media server. The media beans themselves are defined in the SimpleSipStack project; ConvediaMediaBeans is an implementation using Convedia’s media server (The media server talks MSML and MOML over SIP). ConferenceBean leverages the media server and allows applications to create conference calls, while MediaBeanImpl can be used for the playback of announcements, collecting dtmf, recording etc.

All of the actions exposed via the API complete asynchronously. On completion/failure, applications are notified of events using the listener pattern. Listeners can either be added via the application context or directly from code. Listener interfaces are defined at each layer and applications can choose the events they are interested in. Infact, beans defined at each layer in Aloha register themselves as listeners to the corresponding beans in the layer beneath them.

Having provided the basic structure, I will next tackle the testing strategy used in Aloha.

h1

An absolute blunderfest, but a win nonetheless

May 7, 2008

Last week was the last match of the season for the Bedford Chess League. My team needed a match victory to ensure 5th place in the standings, a step up from last place (8th) last year. I was playing black on board 3, matched up against a much lower rated player. As always, the game can be replayed here.

1.d4 d5 2.c4 c6 3.Nf3 Nf6 4.Nc3 e6 5.Bg5 h6 6.Bh4 dxc4 7.a4

I have recently started playing the Semi-Slav as black in online chess but this is my first attempt over the board. In this gambit variation, play is normally extremely sharp with 7.e4 g5 8.Bg3 b5 and white has a strong initiative for the pawn. With my opponent instead playing a4, trying to prevent b5, I tried to take advantage of the hole on b4.

7…Bb4 8.e3 Qa5 9.Qc1? Ne4 10.Bxc4 Nxc3 11.bxc3 Bxc3+ 12.Ke2 Bxa1 13.Qxa1 O-O 14.Rb1 Nd7 15.Nd2 Nb6 16.Bb3 Bd7 17.Be7 Rfe8 18.Bc5 Nd5 19.Nc4 Qa6 20.Ke1 b6 21.Ba3 Rec8 22.e4 Nf4 23.g3 Ng6 24.Nd6 Qa5+ 25.Kf1 Rc7 26.Qb2 Qh5 27.h4 [Diagram]

Until this point, my play might not have been very accurate but it has been enough to garner a near-winning advantage. After going up in material fairly early on, I was pushed onto the defensive as I was behind in development while white was able to exert a fair amount of pressure. After needing to be careful about my queen getting trapped for a few moves, I felt fairly confident that the queen’s jump to the kingside, coupled with the opening up of the position with c5 would settle the game quite quickly.

27…c5??

A huge blunder. Now my queen is trapped. 27…Qf3 would have still maintained my advantage.

28.Bd1! Qxd1+ 29.Rxd1 Bxa4

Against a stronger opponent I would have probably resigned after his 28th move. Instead, I decided to play on with a rook and two pawns against a queen and give my opponent a chance to redeem the favour.

30.Rc1 Rd8 31.e5 Bc6 32.Qd2 Bd5 33.h5 Nf8 34.g4 f6 35.f4 Bf3 36.g5 hxg5 37.fxg5 fxe5 38.Nb5 Rf7 39.Ke1 Bxh5 40.Qh2 Bg6 41.Ke2?

Question marks could have been attached to most of the previous white moves, as at some point or the other, he should have played dxc5, giving me isolated, doubled pawns on the e-file and another weak, isolated pawn on the c file. Now he instead loses another pawn for no gain whatsoever.

It is also quite likely that question marks can be attached to several of my moves as they weren’t the best moves. But I was trying not to make forcing moves; instead providing my opponent with several options and more ways to go wrong.

41…exd4 42.Rh1 Nh7 43.Bc1 a6 44.Nd6 Rfd7 45.Bf4 b5 46.Be5 c4 47.Kd2

Sensing the strength of my connected passed pawns, my opponent offered me a draw. Here was my final blunder of the game. With the other games of the match already completed, I should have looked to my captain to find the match score (It was 2.5-1.5 and a draw in this game would have clinched us the match). The right move on my part would have been to accept the draw at this point. However, feeling that I was better already, I declined the draw and kept playing.

47…c3+ 48.Kc1 d3 49.Qh3 Rxd6 50.Bxd6 Rxd6 0-1

h1

Aloha Baby!

May 2, 2008

I’d like to be, but I am not in fact in Hawaii!

Robbie, as usual, is ahead of me in the announcement, but we have finally open-sourced our SIP Application Server. It still has some rough edges around it in terms of the README etc, so you might have trouble building it from source if you download it right away, but it is finally out there.

Over the course of the past year, I have considered writing about several aspects of the project but was always unsure of how much can be spoken about in the public domain. Now that it is open-sourced, and because I consider it one of the cooler projects I have been involved in, I would now like to take up the chance to write up a series of blog posts talking about various design decisions and patterns in the implementation.

I plan to cover the unit-testing and acceptance-testing strategies, the Spring-ified nature of the application, the message flows, the optimistic concurrency model, the state-storage options and whatever else I can think of. Again, as Robbie mentions, if our paper gets accepted, three of us will be presenting at Agile2008 this year in Toronto about our robustness and performance tests.

In short, stay tuned. Or more likely, stay away!