I've really got into Unit Testing now. The Ruby Unit Testing framework makes it very easy. I've tried unit testing in the past on C++ projects but the lack of a good framework there meant that coding the tests was more laborious than coding the code it was testing.
So far I've written a Bootloader than downloads Hex files to the target firmware. It consists of the following modules:
- low level rs232 extension written in C++ and swig
- higher level rs232 port abstraction written in ruby
- Boot Loader modules to interface with the boot loader firmware embedded in the ARM7 microcontroller
- Intel Hex file decoder
I have been able to test these as follows:
- a test harness to test the low level RS232 extensions by using an RS232 loopback connection
- a test harness to test the higher level RS232 abstration using an RS232 loopback connection
- a test harness to test the Boot Loader module against the real microcontroller
- a test harness to test the Boot Loader module against a 'fake' rs232 port
- a test harness to test the parsing of Intel Hex files
- a test harness to download a 'known good' intel hex file to the microcontroller
I am able to test automatically just about anything here in one of three modes:
- with the real microcontroller, real rs232
- rs2332 loopback
- 'fake'
The 'faker' is what a call a class that is able to emulate a port. Initially I wrote it to emulate an RS232 port in the following way:
- the 'faker' would receive what was being output to the port and check that it was correct
- the 'faker' was able to simulate data being returned from the port
I created a faker that parsed the input and output data from a IO class and hence could test the Bootstrap commands with a fake rs232 port like this:
1 # 2 # Test the Get command 3 # 4 def test_get 5 strConversation = <<EOF 6 # connect 7 < 0x7f 8 > 0x75 9 # command 10 < 0x01 11 # data 12 > 0x11 0x00 0x00 0x75 13 EOF 14 oFd = StringIO.new( strConversation) 15 oPort = RS232Faker.new 16 17 oPort.Run( oFd) do 18 oBoot = BootLoader.new 19 oBoot.Connect( oPort) 20 oResults = oBoot.CommandGet 21 22 assert_equal [0x11,0x00,0x00], oResults 23 end 24 end
Lines in the fake script that start with a '<' is data that should be transmitted and lines that start with a '>' should be received.
The Bootloader module itself is written such that it can connect to either a real rs232 port or this fake one.
Within the Bootloader modules was a particularly complex method that converted memory address ranges into an array of sector numbers that needed erasing. I exposed this method so that I was able to test it specifically in the Unit Test.
The faker can be generalised beyond rs232 to handle anything that inputs and outputs. It doesn't even need to run a fixed script, it could contain heuristics to emulate hardware such as a temperature control channel and test whether the code does actually control temperatures.
I'm toying with the idea of testing my embedded C code by compiling it into a Ruby extension and calling it from Ruby Test Harnesses. The code would be compiled by gpp both for the ruby extension and the real hardware. This should provide thorough testing of the logical aspects of the code (e.g. servo control calculations) and may only be lacking in the following areas:
- it would not test the 'real time' aspects of the C code (interraction with interrupts etc)
- it would not test for problems in the code generation for the real firmware
- it would not test for differences between the processor architectures, e.g. number of bits in an integer, although the ARM is a 32 bit microcontroller.
However, this should provide a satisfying amount of testing.
Another problem with this is that I would have ruby calling a ruby extension in C which would in turn call 'faker' objects written in ruby. I am unsure how to go about doing this and I am unsure whether the faker objects would be able to raise exception back up to the main ruby script. It may be able to do this in a flaky and memory-leaking way but that isn't too much of a problem in a test harness: I am testing the algorithms, not the memory leaks in the test harness.
Conclusion: Unit Testing FTW

