POPing back


Way back in mid November, before we dumped our building's managing agent for being worse than useless and possibly stealing from us, I was working on some POP3 code. I had some down time today so I decided to drop back into it and see if I could move things along a little.

In summary, having lots of tests helped...

I haven't done any development on the POP3 code for 3 months. I've updated it when dependant libraries changed, but I haven't added any functionality. The first thing to do was run the tests, they all ran, they all passed, and boy aren't there a lot of them... The POP3 code was the first substantial piece of code that I developed strictly test first.

The first code I wrote today was a new test. With all the existing tests staring at me I couldn't just dive in and write code without having a failing test. The weight of existing tests pushed me in the right direction; if I had written code without a test I would have started down the slippery slope and this code was so tested that it just seemed wrong not to.

I wasn't expecting that effect. Of course, the test was easy to write because I had so many other tests around the code that I was about to write. I had mock objects to provide the scaffolding that I needed. Not only did the existing tests make it difficult for me to even consider not writing a test but they also made it easy to write the new test... Result.

The code I decided to write today was the asynchronous version of the POP3 client. This is the code that James Antill was concerned about way back when. I think his fears were unfounded. Due to the 'push' nature of the async handling that we need to deal with when working with Win32 IO Completion Port style async IO the code looks quite different from the 'begin_op', 'finish_op' pair that James anticipated. It's true we have a 'begin_op' which does the sending of the command to the server but the 'finish_op' is a general purpose 'process response' kinda thing. At present, and we're only 3 operations and several tests in, the pattern is as follows; caller knows what they want to do and calls, for example, STAT() this sends the STAT request to the server and sets up the state machine in the client. Time passes. Data comes back. We push it into the client's generic 'process response' function and it accumulates enough data to run the state machine and the cogs turn. Eventually accumulate data returns a state that indicates the process has completed. Results can then be retrieved by the caller calling the appropriate function...

Seems to work... Seems to handle the worse case scenario of 'we get a response back a byte at a time' and the best case scenario of 'we get a response to this command and the next all at once'...

There will be scope for refactoring the common code out of the sync client and the async client but that's a story for another day.

Leave a comment