Hacking our way to the first test

| 2 Comments
So I have some time on my hands and I've decided that The Server Framework code could do with some tests but before I can do that I need to refactor because the code was written before I became test infected and, well, it's not as good as it could be...

Step one, as always, is to work on decoupling the code to the point that it's easy to test. This generally involves breaking the code apart into more cohesive chunks and then writing tests for the chunks. At this point we rely on the black box server tests that we have to make sure that we don't break too much while we're hacking.

The latest freely available socket server code lives here. As you can see the main attraction is CSocketServer; a class that does way too much. The socket server class is responsible for managing a pool of sockets, managing another pool of IO buffers, dealing with all the async IO callbacks that occur as we send and receive data, letting us connect to things, allowing things to connect to us, firing events into derived classes when stuff happens and more. I know why the code's like this, it just kinda grew and it was easier to allow it to grow where it wanted to rather than redesign at each stress point to preserve some semblance of design. It's not that bad but it could be better.

Step one is to break it apart into several classes that have more focus. These classes can then be put back together to form a composite that provides the same functionality as the existing CSocketServer class for those that need that functionality and can be used alone when required. Once we have several cohesive classes we'll be able to test each part of functionality in isolation, this will make for easier testing... I hope...

So first we look for the natural fractures in the code; things that look like they can be pulled apart easily. The first one of these is the IO buffer allocator. Right now our socket server uses protected inheritance to inherit from CBufferAllocator; as far as it and those derived from it are concerned the server IS A buffer allocator, hmm, that smells.

Pulling the buffer allocation functions out of the server mean that we have to pass a buffer allocator into the server on construction, that ol' parameterise from above thing again. This makes the server a little more cohesive, it no longer IS a buffer allocator it simply uses one, and allows us to share the allocator between servers if we want to, this may allow more efficient pooling...

Next on the list of really obvious fault lines is the fact that we have some code that uses the socket server purely to handle outgoing connections. The code uses the fact that the CSocketServer class can initiate outgoing connections and handle the async IO for them and that if you never bother to call StartAcceptingConnections it never actually listens for incoming connections... Unfortunately you still have to provide it with an address and port to listen on when you create it even though these are never used because we're using the server in a way that wasn't really intended...

Sounds like we need two classes here; one that has the accept thread and incoming connection handling and one that has the bulk of the IO handling and the outgoing connection stuff in it. The incoming connection handler can derive from the outgoing connection handler, it needs the IO handling and initiating an outgoing connection is done by one function so I don't feel too bad about doing it this way... Once this is done we can create the CAsyncSocketConnectionManager object if we just want to do client stuff and the CSocketServer when we want to do server stuff... Like I said, this isn't the best of designs but it's better than what we have right now...

The CAsyncSocketConnectionManager contains code that manages a pool of per socket data structures. This operates in a very similar way to the pool of per IO operation data structures (the IO buffers) so we could split the pool management code out into a socket allocator which mirrors the buffer allocator. Again we can pass this in at construction time so that we can share it between servers if we want to (or, more usefully, just pass in a mock version when we're testing...)

So now we have four objects rather than one; each is more focused on one thing yet when plugged together they provide the same functionality that we had before. The four objects are easier to test because each test harness has to test less functionality and if we slip some interfaces in we can use mock versions of the objects that aren't under test to help test the object that is under test...

Off to write those first tests...

2 Comments

Hi Len,
is there any chance you will go public with the extensive modifications you're applying to your Socket Server ? Any ETA if yes ?
Thanks.
Roberto

I don't know. I expect I may well do at some point but there's no ETA at present.

Leave a comment