Peter's Blog

Redefining the Impossible

Testing 123


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:

  1. with the real microcontroller, real rs232
  2. rs2332 loopback
  3. '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


Filed under: ruby testing unit

Comments are Closed