Garlic Software

Programming

Arduino Intro for iOS Presentation

I coordinated a presentation for the Silicon Valley iOS Meetup on Arduino and iOS. My small part of the presentation centered on an introduction to Arduino and the slides are included here.
iOSArduino

Unit testing Presentation at 360iDev

I gave a talk at the 360iDev conference in Denver, Colorado covering adding OCUnit unit testing to your iOS or Mac project. This was the session description:

Unit testing your code ensures that it does what it was designed to do. This presentation will cover methods of unit testing (OCUnit) for iOS and Mac developers using a hands-on approach. Code will be discussed and distributed for everyone to try out. By the end of this session you will know what a unit test is, why you should test, and how to write one to make your code better.


The slides and the code samples shown during the session are available here: UnitTest

Reducing power usage on an AVR ATTiny4

This is an article about how I reduced the battery power usage of the cool iCufflinks made by Adafruit Industries.

Background

The iCufflinks use an Atmel ATtiny4 microcontroller (MCU) as the brains to controlling the LED lighting pattern. The MCU is an 8-bit processor with 32 bytes of SRAM, only a handful of registers, and 512 bytes of flash for program storage. The stack is stored in the SRAM so you don’t really get to use it for anything.

The original hardware design and software are all open source and can be found on the Adafruit GitHub. One of the things about the design is that it runs on CR1220 batteries and it is recommended that they be changed after 24 hours of use. That is what got me thinking that I could improve this product to increase the amount of time between battery changes.

I have also never read nor written assembly code for an AVR processor and the last time I probably looked at assembly was 386 stuff about 20 years ago. So excuse any minor assembly style issues. I was temped to rewrite the code in C but with the limited flash space I had to rule this out. Had this been a ATtiny9 with 1k bytes I would have gone this route. The small overhead that AVR Studio introduces was just a tiny bit too much for this limited memory space.

Baseline

I needed to measure the baseline power usage of the circuit so I could see what my changes were doing. The problem was that I didn’t have any iCufflinks nor any ATtiny4 devices. So I had to order some components from Digi-Key. I also ordered the same LED model that was used in the cufflinks (this was really more of a guess based on the schematics). Once the order arrived I set to building my test circuit shown in the picture below. The version 1.0 code is running on the left and my modified code is running on the right.

IMG_0538


Now that the circuit was built, I measured the current draw to get my baseline usage. Note that when I’m measuring I’m only measuring one of the little circuits at a time. In the rest of this article I will be mainly using the Average reading when comparing results.

MaxAverageMin
Whole Circuit1.923 mA0.848 mA0.458 mA
MCU Only465.29 μA452.53 μA442.65 μA

I’m making all these measurements with a Fluke 289 multimeter using the mA and μA current settings. Power is coming from a bench top supply at 3V.

Sleep mode

The first thing I noticed about the code was that the processor was active all the time and was basically just constantly counting in a loop intended to introduce a delay in the code.
Here is the delay loop which is intended to make a 17ms delay.

   ; delay!
   ldi delayms, DELAYTIME ; delay ~17 ms
DELAY:
   ldi delaycnt1, 0xFF
   DELAY1MS: ; this loop takes about 1ms (with 1 MHz clock)
      dec delaycnt1 ; 1 clock
      cpi delaycnt1, 0 ; 1 clock
      brne DELAY1MS ; 2 clocks (on avg)
   dec delayms
   cpi delayms, 0
   brne DELAY


As far as power goes, this is really inefficient as it is just sitting there burning power the whole time counting. It would be much better to have the processor sleep for those 17ms. So I introduced the Idle sleep mode to the code. Since we want to keep the PWM constantly running to drive the LED we can’t shut down the chip completely in sleep and need to keep CLKIO active but we can shut down CLKCPU. As it turns out there is only one sleep mode called Idle Sleep Mode where the CLKIO is left running. This code snippet put in your reset vector will enable sleep mode and the default is Idle mode.

   ; enable sleep mode
   ldi temp, (1<<SE) ; by default the mode is 000 Idle
   out SMCR, temp

The other issue is how to wake up from sleep so we can continue the work. The PWM is using our one counter so we can’t use that and there is no external component to trigger the interrupt pin INT0. So the only option is to repurpose the watchdog timer to generate an interrupt. The default watchdog timer is set for 2k cycles at 128kHz which is about 16ms. That delay is close enough to the 17ms of the original code so I’m going to just use that default value. The following code snippet put in your reset vector will setup the watchdog timer in interrupt mode and enable interrupts.

   ; setup watchdog
   ldi temp, 0xD8 ; write signature
   out CCP, temp
   ldi temp, (0<<WDE)|(1<<WDIE) ; set watchdog in interrupt mode
   out WDTCSR, temp

   sei ; enable global interrupts

Now that we have sleep mode enabled and the watchdog all setup, the delay loop can now be replaced with a simple reset of the watchdog timer and a sleep call. You also need to add in the watchdog interrupt vector and the interrupt handler, see the code where WDT is defined and used.

   ; reset the watchdog timer to full value and sleep until it pops an interrupt
   wdr
   sleep

Measuring the power savings with just the sleep mode added is pretty significant.

BaselineModified
MCU Only452.53 μA191.31 μA

Saves about 261 μA.

Enable pull-ups

Next up is a little trick I learned about while reading an application note on picoPower (see link in references section at the end). PicoPower is one of Atmel’s power saving technologies in some of the newer ATtiny devices (not the ATtiny4). It turns out that the chip wastes power switching on I/O pins if the pins are floating and don’t have any pull-up resistor on them. This is also mentioned in the data sheet. The easiest method to fix this is to enable the built-in pull-ups on unused port pins. Since we are using PB0 for the PWM we wont touch that one, but everything else is unused and can be fixed. The following code snippet in your reset vector is all you need.

   ; setting all pullups on unused pins (for power savings)
   ldi temp, (1<<PUEB3)|(1<<PUEB2)|(1<<PUEB1)
   out PUEB, temp

BaselineModified
MCU Only452.53 μA176.11 μA

Saves about 15 μA.

Slow the clock down

The ATtiny4 can run at up to 8MHz with its’ built-in oscillator. From the factory it is set with a clock prescale of 8 so it is really running at 1MHz. The main thing this code does is load a value from memory and set the PWM to this value. This does not need to run at 1MHz and we can really slow this down to the slowest possible setting and it will still be plenty fast for our purposes. The largest clock division factor available is 256 which will result in the clock running at 32kHz. So again, adding this little code snipped in the reset vector will slow the clock down.

   ; changing clock prescale to slow down the processing power (for power savings)
   ldi temp, 0xD8 ; write signature
   out CCP, temp
   ldi temp, (1<<CLKPS3)|(0<<CLKPS2)|(0<<CLKPS1)|(0<<CLKPS0) ; scale to divide by 256
   out CLKPSR, temp

BaselineModified
MCU Only452.53 μA139.76 μA

Saves about 36 μA.

Shrink the code

Looking at the compile time statistics you can see that the largest part of the compiled app is a huge block of PWM values. This is the original code size before I changed anything.

   ATtiny4 memory use summary [bytes]:
   Segment Begin End Code Data Used Size Use%
   ---------------------------------------------------------------
   [.cseg] 0x000000 0x00015e 50 300 350 512 68.4%

The program memory space in the chip is only 512 bytes and this block was taking up 300 bytes. This is what it looks like:

PULSETAB:
.db 255, 255, 255, 255, 255, 255, 255, 255, 252, 247, 235, 235, 230, 225, 218, 213, 208, 206, 199, 189, 187, 182, 182, 177, 175, 168, 165, 163, 158, 148, 146, 144, 144, 141, 139, 136, 134, 127, 122, 120, 117, 115, 112, 112, 110, 110, 108, 103, 96, 96, 93, 91, 88, 88, 88, 88, 84, 79, 76, 74, 74, 72, 72, 72, 72, 69, 69, 62, 60, 60, 57, 57, 57, 55, 55, 55, 55, 48, 48, 45, 45, 43, 43, 40, 40, 40, 40, 36, 36, 36, 33, 33, 31, 31, 31, 28, 28, 26, 26, 26, 26, 24, 24, 21, 21, 21, 21, 20, 19, 19, 16, 16, 16, 16, 14, 14, 14, 16, 12, 12, 12, 12, 12, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 7, 9, 9, 9, 12, 12, 12, 14, 14, 16, 16, 16, 16, 21, 21, 21, 21, 24, 24, 26, 28, 28, 28, 31, 36, 33, 36, 36, 40, 40, 43, 43, 45, 48, 52, 55, 55, 55, 57, 62, 62, 64, 67, 72, 74, 79, 81, 86, 86, 86, 88, 93, 96, 98, 100, 112, 115, 117, 124, 127, 129, 129, 136, 141, 144, 148, 160, 165, 170, 175, 184, 189, 194, 199, 208, 213, 220, 237, 244, 252, 255, 255, 255, 255, 255, 255, 255, 0

I looked at that data and saw so many duplicate values right next to each other that I though I could at least halve the data and still retain the same visual look at the LED. It turns out that the big 300 byte block of PWM values in the code is a bit redundant. So I chopped the data in half while rounding down.

PULSETAB:
.db 255, 255, 255, 255, 250, 235, 228, 216, 207, 194, 185, 180, 171, 164, 153, 145, 143, 138, 131, 121, 116, 112, 110, 106, 96, 92, 88, 88, 82, 75, 73, 72, 71, 66, 60, 57, 56, 55, 52, 47, 44, 42, 40, 38, 36, 33, 31, 30, 27, 26, 25, 23, 21, 20, 19, 16, 16, 14, 14, 12, 12, 11, 9, 9, 8, 7, 7, 6, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 5, 7, 7, 8, 9, 12, 13, 15, 16, 18, 21, 22, 25, 28, 30, 33, 36, 40, 43, 46, 53, 55, 59, 63, 69, 76, 83, 86, 90, 97, 106, 116, 125, 129, 138, 146, 162, 172, 186, 196, 210, 228, 248, 255, 255, 255, 0

This reduced nicely and with my other changes in the code the main code only grew by 18 bytes.

   ATtiny4 memory use summary [bytes]:
   Segment Begin End Code Data Used Size Use%
   ---------------------------------------------------------------
   [.cseg] 0x000000 0x0000e8 68 150 218 512 42.6%

Not only does this save 150 bytes of precious space, but there is one more benefit. Since I had changed the code to sleep between changes of PWM values, I needed to now double the sleep time because I had half the data. Turns out this is really easy by just changing the watchdog timer to go twice as long. It was going at 2k cycles (about 16ms) and this change moves that to 4k cycles (about 32ms). This snippet replaces the previous watchdog setup.

   ; setup watchdog
   ldi temp, 0xD8 ; write signature
   out CCP, temp
   ldi temp, (0<<WDE)|(1<<WDIE)|(1<<WDP0) ; set watchdog in interrupt mode and 4k cycles
   out WDTCSR, temp

This code change doesn’t really save much at all in power as we were already spending most of the time sleeping, but there is a tiny improvement.


BaselineModified
MCU Only452.53 μA138.01 μA

Saves about 2 μA.

Results

Here are the final results of these changes to improve battery life.


BaselineModified
Whole Circuit0.848 mA0.533 mA
MCU Only452.53 μA138.01 μA

Saves about 315 μA.

As you can see from the data there is about a 395 μA draw from the LED and resistor that makes up the rest of the circuit. With the current hardware there is nothing I can do about this draw.

The overall effect this has on the product is that the 24 hour time between battery changes can be upped to 38 hours. That is a pretty good power savings for the day.

Resources

My code on GitHub

References

Atmel AVR ATtiny4 Data Sheet
Atmel Application Note AVR4013: picoPower Basics
Atmel AVR Studio 5
Fluke 289 Multimeter

Unit testing with cppunit

I like to use various types of unit testing frameworks (mostly in the xUnit family) for testing my code to make sure it works as intended. This article is going to show how to write some tests that match the code you write using the C++ language on a Macintosh computer. You could apply these tests either in a test driven development (TDD) environment or after the code has been written which is what I’ve seen the majority of the time.

What is a unit test?

I think of a unit test as a piece of code that tests another piece of code and the test runs every time a build is done. Ideally the pieces are small so the tests and the code are easy to verify. Once you build up a large collection of test and it begins to approach 100% code coverage, you will feel confident that your code does what it intended to do.
Here are a couple examples of cppunit unit tests that we will use later:

    1: CPPUNIT_ASSERT_MESSAGE("check pointer is not null", test3 != NULL);
    2: CPPUNIT_ASSERT_EQUAL(false, test5->fNegative);
    3: CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(3), test5->fIntPart);
    4: CPPUNIT_ASSERT_DOUBLES_EQUAL(0.14, test5->fFractPart,  FixedPoint::kMarginOfError());

Where to get cppunit

You can get the latest version of cppunit here: Sourceforge
At the time of this writing, the latest stable release is 1.12.1 which will be used for all of the examples in this article. Compiling the cppunit library is the standard UNIX style:

./configure
make
sudo make install

Once you have cppunit installed we can begin to use it with Xcode.

Getting started

I’m going to show how to setup a few tests using sample C++ code. The reason this is done in C++ is that you can use this on an iPhone, Android, Macintosh, or Windows computer as it is cross platform. If your familiar with the model-view-controller design pattern, the model portion is a good candidate for writing in a cross platform fashion. The code we are going to test models a fixed point number.

The Fixed Point Problem

Design and implement a class to work with the following encoding of a fixed-point number. We often work with client hardware that uses proprietary or unusual protocols or data formats. For this exercise, assume that we are developing software that receives data values from a piece of client-provided hardware in the binary-encoded format described below. The task at hand is to create a class that can correctly encode and decode those values and make them usable by other software.
In this instance, a fixed-point number is packed into a 32-bit unsigned integer as follows:
     s i i i i i i i   i i i i i i i i   i f f f f f f f   f f f f f f f f
where:

s : sign bit (1 == negative)
i : 16 integer bits
f : 15 fraction bits

Examples:

ValueFixed Point encoding
1.00x00008000
-1.00x80008000
2.00x00010000
-2.50x80014000
3.140x000191eb
100.990x00327eb8

Your class should support at least the following operations:
  • Create Fixed Point object from floating point value
  • Create Fixed Point object from packed 32-bit value
  • Idiomatic conversion to string (as <<optional sign>><<integer part>>.<<fractional part>>) For example, an instance of your Fixed Point class initialized from an encoded value of 0x80008000 would be converted to the string "-1.0".
  • Convert value to closest floating point equivalent
This problem is derived from a 2008 programming challenge from Art & Logic. This is a good example to use as you want to show that your class not only adheres to the requirements, but it is proven correct.

Quick Xcode setup

We need to make a couple changes to a default Xcode project to get cppunit included. Create a new project in Xcode or use an existing one. I chose the command line tool template for this example but this can also be used with the iPhone or other project types. A link to download the complete project with code is at the end of this article. Once your new project is created or you opened an existing project in Xcode, select Edit Project Settings from the Project menu as shown.

XcodeScreenSnapz001

You can change the project settings to search the include path /usr/local/include and library path /usr/local/lib which are the default install locations for cppunit. I also include a debugging flag (see screenshot below) that is included with the Debug configuration only so that I can trigger the tests in the Debug build only and make sure they are not used in the Release build.

XcodeScreenSnapz002

Once you have the project settings modified, we need to add in the library file to link against. One way to do this is to right-click (control click) on one of the folders with your source code and select Add then Existing Frameworks….

XcodeScreenSnapz003

Change the popup menu at the top of the sheet to Dylibs and then select libcppunit.dylib. You don’t have to use the dylib here and you can link against the .a version. There are pros and cons to either approach which you should understand before using them. This article will not cover these issues and we will use the dylib for simplicity.

XcodeScreenSnapz004

Now that the project file is all setup with the necessary pieces to run unit tests we will look at some code.

Some code

The strategy I’m going to use it to have all of the tests run every time the Debug version of the code is run. Then once the code is ready, we compile it in Release mode so that the tests will be removed. Once we have a new project we want to modify the main.cpp to make it run the tests.

We add a bunch of headers:

    1: #if defined(qDebug)
    2: #include <cppunit/BriefTestProgressListener.h>
    3: #include <cppunit/CompilerOutputter.h>
    4: #include <cppunit/extensions/TestFactoryRegistry.h>
    5: #include <cppunit/TestResult.h>
    6: #include <cppunit/TestResultCollector.h>
    7: #include <cppunit/TestRunner.h>
    8: #include <stdexcept>
    9: #endif // qDebug

And then we add the test runner at the beginning of the main method. This is all just a template that I copy into each project.

    1: #if defined (qDebug)
    2: // Create the event manager and test controller
    3: CPPUNIT_NS::TestResult controller;
    4:
    5: // Add a listener that colllects test result
    6: CPPUNIT_NS::TestResultCollector result;
    7: controller.addListener( &result );        
    8:
    9: // Add a listener that print dots as test run.
   10: CPPUNIT_NS::BriefTestProgressListener progress;
   11: controller.addListener( &progress );      
   12:
   13: // Add the top suite to the test runner
   14: CPPUNIT_NS::TestRunner runner;
   15: runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() );
   16: runner.run( controller );
   17:
   18: // Print test in a compiler compatible format.
   19: CPPUNIT_NS::CompilerOutputter outputter( &result, std::cerr );
   20: outputter.write(); 
   21: #endif // qDebug

Once these changes are in, you should be able to build and run the project and see the OK message in the console output. You can open the console by selecting Console from the Run menu. Then click the Build and Run button and you should see output similar to the following:

Running…
OK (0)

The 0 (zero) after the OK is the number of tests that ran and in this case we don’t have any yet so we only see that the unit testing framework is setup and ready to go. We are first going to look at a single method to understand what it is doing and what we need to do to test it. I need to give a little background on the class definition here for understanding.

    1: typedef unsigned short UInt16;
    2: typedef unsigned long int UInt32;
    3:
    4: class FixedPoint {
    5: public:
    6:         // construction, assignment and destruction...
    7:         explicit FixedPoint(double inValue);
    8:         explicit FixedPoint(UInt32 inValue);
    9:         explicit FixedPoint(const FixedPoint& inValue);
   10:         FixedPoint& operator=(const FixedPoint& rhs);
   11:         virtual ~FixedPoint(void) {;}
   12:
   13:         // return the value as a string, float, or int
   14:         std::string StringValue(void) const;
   15:         double FloatValue(void) const;
   16:         UInt32 EncodedValue(void) const;
   17:
   18: private:
   19:         bool    fNegative;      // true if the number is negative
   20:         UInt16  fIntPart;       // integer part of the number
   21:         float   fFractPart;     // fractional part of the number
   22:
   23:         // given the number of bits available for this class, it has limits
   24:         // to the size of the number it can represent. These constants are used for
   25:         // bounds checking any value assigned.
   26:         static const double kMinValue(void) {return -65535.99997;}
   27:         static const double kMaxValue(void) {return 65535.99997;}
   28:         static const float kMarginOfError(void) {return 0.00003;}
   29: };

The class uses three private member variables to track the three parts of the fixed point number: fNegative, fIntPart, and fFractPart. So the method that we are going to look at in depth deals with constructing an object with a floating point number.

    1: FixedPoint::FixedPoint(double inValue) {
    2:         if (inValue > kMaxValue())
    3:                 throw std::out_of_range("value is greater than the kMaxValue possible");
    4:         if (inValue < kMinValue())
    5:                 throw std::out_of_range("value is less than the kMinValue possible");
    6:
    7:         fNegative = inValue < 0.0;
    8:
    9:         if (fNegative)
   10:                 inValue = -inValue;
   11:
   12:         double tint = 0.0;
   13:         double tfract = modf(inValue, &tint);
   14:
   15:         fIntPart = static_cast<UInt32> (tint);
   16:         fFractPart = static_cast<float> (tfract);
   17: }

The method above has three exits that we want to test: the two throws (lines 3 and 5) and the successful return (line 17). Not only do we want to test the returns, but also the math and the way the three internal variables are setup after construction. To test the throws you can use the CPPUNIT_ASSERT_THROW test macro. An example is shown below.

    1: FixedPoint* special100 = NULL;
    2: CPPUNIT_ASSERT_THROW(special100 = new FixedPoint(65535.999971), std::out_of_range);

What this is doing is attempting to create a new object with a value (65535.999971) that is out of range for the class, in this case greater than kMaxValue (65535.99997). The other part you might want to test that it doesn’t throw when given a value within range, for this you would use CPPUNIT_ASSERT_NO_THROW. While this is not strictly needed for testing I find that it is a good idea to test both cases if you are going to use exceptions.

    1: CPPUNIT_ASSERT_NO_THROW(special100 = new FixedPoint(65535.99997));

The next type of test is to see if all of the internal variables are setup correctly after a new object is created. The problem statement above included several numbers and their encoded values for testing against. We will use all of them in the tests. While we really don’t need to include so many of the exact same types of tests, testing against all of the sample numbers provided in the problem, we will just for the sake of showing that it solves the given problem with the values provided. In real world testing you might not want to include multiple copies of the same test as you will have to maintain them all when the rest of the code changes and having multiple tests of the same thing doesn’t serve any real purpose (it’s not more accurate because there are many of the same tests).

    1: FixedPoint special5(3.14);
    2: CPPUNIT_ASSERT_EQUAL(false, special5.fNegative);
    3: CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(3), special5.fIntPart);
    4: CPPUNIT_ASSERT_DOUBLES_EQUAL(0.14, special5.fFractPart, FixedPoint::kMarginOfError());

This test above is calling the same float constructor with a value (3.14) and then testing all of the internal components to see if they were setup as expected. The macro CPPUNIT_ASSERT_EQUAL makes these tests pretty straight forward to read. The only trick you need to remember is to typecast any ambiguous types so that the comparison is using the exact same types. If you forget this, the compiler should display error messages about type mismatch. Another special note is that the macro CPPUNIT_ASSERT_DOUBLES_EQUAL takes a third parameter which is the margin of error for the floating point comparison. The reasons why you need this for floating point numbers are beyond this article, but suffice to say that you need it and it’s important.

Below is the full unit test for the float value constructor. Note that several of these tests are testing the exact same thing and are not really needed nor desired for production code. You want to have the minimum number of tests that test all of the functionality but not duplicates. All of the extra tests were included just to show the results for the numbers provided in the problem statement above. Also any special edge cases, you will want to call out as in this example at line 52.

    1: void FixedPointTest::floatConstructorTest(void) {
    2:         FixedPoint special1(1.0);
    3:         CPPUNIT_ASSERT_EQUAL(false, special1.fNegative);
    4:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(1), special1.fIntPart);
    5:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, special1.fFractPart, FixedPoint::kMarginOfError());
    6:
    7:         FixedPoint special2(-1.0);
    8:         CPPUNIT_ASSERT_EQUAL(true, special2.fNegative);
    9:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(1), special2.fIntPart);
   10:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, special2.fFractPart, FixedPoint::kMarginOfError());
   11:
   12:         FixedPoint special3(2.0);
   13:         CPPUNIT_ASSERT_EQUAL(false, special3.fNegative);
   14:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(2), special3.fIntPart);
   15:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, special3.fFractPart, FixedPoint::kMarginOfError());
   16:
   17:         FixedPoint special4(-2.5);
   18:         CPPUNIT_ASSERT_EQUAL(true, special4.fNegative);
   19:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(2), special4.fIntPart);
   20:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, special4.fFractPart, FixedPoint::kMarginOfError());
   21:
   22:         FixedPoint special5(3.14);
   23:         CPPUNIT_ASSERT_EQUAL(false, special5.fNegative);
   24:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(3), special5.fIntPart);
   25:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.14, special5.fFractPart, FixedPoint::kMarginOfError());
   26:
   27:         FixedPoint special6(100.99);
   28:         CPPUNIT_ASSERT_EQUAL(false, special6.fNegative);
   29:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(100), special6.fIntPart);
   30:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.99, special6.fFractPart, FixedPoint::kMarginOfError());
   31:
   32:         FixedPoint special7(0.0);
   33:         CPPUNIT_ASSERT_EQUAL(false, special7.fNegative);
   34:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(0), special7.fIntPart);
   35:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, special7.fFractPart, FixedPoint::kMarginOfError());
   36:
   37:         FixedPoint special8(-65535.99997);
   38:         CPPUNIT_ASSERT_EQUAL(true, special8.fNegative);
   39:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(65535), special8.fIntPart);
   40:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.99997, special8.fFractPart, FixedPoint::kMarginOfError());
   41:
   42:         FixedPoint special9(65535.99997);
   43:         CPPUNIT_ASSERT_EQUAL(false, special9.fNegative);
   44:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(65535), special9.fIntPart);
   45:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.99997, special9.fFractPart, FixedPoint::kMarginOfError());
   46:
   47:         FixedPoint special10(65535.99994);
   48:         CPPUNIT_ASSERT_EQUAL(false, special10.fNegative);
   49:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(65535), special10.fIntPart);
   50:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.99994, special10.fFractPart, FixedPoint::kMarginOfError());
   51:
   52:         // Note that this value is not round tripped here as -0.0 is no different from 0.0
   53:         FixedPoint special11(-0.0);
   54:         CPPUNIT_ASSERT_EQUAL(false, special11.fNegative);
   55:         CPPUNIT_ASSERT_EQUAL(static_cast<UInt16>(0), special11.fIntPart);
   56:         CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, special11.fFractPart, FixedPoint::kMarginOfError());
   57:
   58:         FixedPoint* special100 = NULL;
   59:         CPPUNIT_ASSERT_THROW(special100 = new FixedPoint(65535.999971), std::out_of_range);
   60:         delete special100;
   61:
   62:         CPPUNIT_ASSERT_THROW(special100 = new FixedPoint(-65535.999971), std::out_of_range);
   63:         delete special100;
   64:
   65:         CPPUNIT_ASSERT_THROW(special100 = new FixedPoint(65536.0), std::out_of_range);
   66:         delete special100;
   67:
   68:         CPPUNIT_ASSERT_THROW(special100 = new FixedPoint(-65536.0), std::out_of_range);
   69:         delete special100;
   70:
   71:         CPPUNIT_ASSERT_NO_THROW(special100 = new FixedPoint(65535.99997));
   72:         delete special100;
   73:
   74:         CPPUNIT_ASSERT_NO_THROW(special100 = new FixedPoint(0.999971));
   75:         delete special100;
   76: }

Creating a test suite

To get all of the tests to run, you need to add them to a test suite which is added to the test runner. There are several macros that you use to setup all of this for you and an example is shown below for adding in the two tests discussed in this article. This test class can either be in the same file as your class methods or in a new file. For this example the tests are included at the end of the class methods to keep all the code in the same place. While this does make the file longer, it also makes it easy for developers to update the tests whenever they change the code.

    1: #if defined(qDebug)
    2: #include <cppunit/extensions/HelperMacros.h>
    3:
    4: class FixedPointTest : public CPPUNIT_NS::TestFixture {
    5:         CPPUNIT_TEST_SUITE( FixedPointTest );
    6:         CPPUNIT_TEST( floatConstructorTest );
    7:         CPPUNIT_TEST( encodedValueTest );
    8:         CPPUNIT_TEST_SUITE_END();
    9:
   10:         public:
   11:                 void setUp(void);
   12:                 void tearDown(void);
   13:
   14:         protected:
   15:                 void floatConstructorTest(void);
   16:                 void encodedValueTest(void);
   17:
   18:         private:
   19:                 FixedPoint* test1;
   20: };
   21:
   22: CPPUNIT_TEST_SUITE_REGISTRATION( FixedPointTest );
   23:
   24: void FixedPointTest::setUp(void) {
   25:         test1 = new FixedPoint(static_cast<UInt32>(0x00008000));
   26: }
   27:
   28: void FixedPointTest::tearDown(void) {
   29:         delete test1;
   30: }
   31:
   32: void FixedPointTest::floatConstructorTest(void) {}
   33: void FixedPointTest::encodedValueTest(void) {}
   34: #endif // qDebug

The important macro calls of CPPUNIT_TEST_SUITE, CPPUNIT_TEST_SUITE_END, CPPUNIT_TEST, and CPPUNIT_TEST_SUITE_REGISTRATION are used to define the test suite start and stop, add tests to the suite, and add the suite to the runner. To add a new test you only need to add the line for the macro CPPUNIT_TEST (line 7), add in the method declaration (line 16), and write the test method (line 33).

Another nice feature of cppunit is that each test class can have a setUp method which will be called before every test that is run and a tearDown method that will be called after every test. This allows you to setup other objects for testing and not have to write the code in each test case. I use that strategy above to setup a test value that is then used in all of the test cases. Below I add many more test values to to the test class and setUp method.

Another Test

This next example is a little easier to test as it is only returning a value and has no extra exits from the method.

    1: // This method will return a packed 32-bit representation of the fixed point number
    2: UInt32 FixedPoint::EncodedValue(void) const {
    3:         UInt32 val = fIntPart << 15;
    4:
    5:         val |= static_cast<UInt32> (fFractPart * static_cast<float> (0x8000));
    6:
    7:         if (fNegative)
    8:                 val |= 0x80000000;
    9:
   10:         return val;
   11: }

The easiest way to test this method is to check for the round trip of the original value set. For convenience I have setup several test values within the FixedPointTest class detailed above. You’ll also note that the test values include all of the test values given in the original problem statement.

    1: void FixedPointTest::setUp(void) {
    2:         test1 = new FixedPoint(static_cast<UInt32>(0x00008000));
    3:         test2 = new FixedPoint(static_cast<UInt32>(0x80008000));
    4:         test3 = new FixedPoint(static_cast<UInt32>(0x00010000));
    5:         test4 = new FixedPoint(static_cast<UInt32>(0x80014000));
    6:         test5 = new FixedPoint(static_cast<UInt32>(0x000191eb));
    7:         test6 = new FixedPoint(static_cast<UInt32>(0x00327eb8));
    8:         test7 = new FixedPoint(static_cast<UInt32>(0x00000000));
    9:         test8 = new FixedPoint(static_cast<UInt32>(0xFFFFFFFF));
   10:         test9 = new FixedPoint(static_cast<UInt32>(0x7FFFFFFF));
   11:         test10 = new FixedPoint(static_cast<UInt32>(0x7FFFFFFE));
   12:         test11 = new FixedPoint(static_cast<UInt32>(0x80000000));
   13: }
   14:
   15: void FixedPointTest::encodedValueTest(void) {
   16:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0x00008000), test1->EncodedValue());
   17:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0x80008000), test2->EncodedValue());
   18:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0x00010000), test3->EncodedValue());
   19:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0x80014000), test4->EncodedValue());
   20:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0x000191eb), test5->EncodedValue());
   21:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0x00327eb8), test6->EncodedValue());
   22:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0x00000000), test7->EncodedValue());
   23:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0xFFFFFFFF), test8->EncodedValue());
   24:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0x7FFFFFFF), test9->EncodedValue());
   25:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0x7FFFFFFE), test10->EncodedValue());
   26:    CPPUNIT_ASSERT_EQUAL(static_cast<UInt32>(0x80000000), test11->EncodedValue());
   27: }

It is important to note that this test is very simple because all it is testing is the output of the method. We don’t need to test the constructors or that the internal values are setup correctly as those are all handled by the appropriate test cases elsewhere. I’d also like to note that this number of tests is overkill and should be pared down to a smaller list of necessary tests along with the edge cases. I’m showing the extra ones here just because they were all in the problem statement.

Results

Once you have added several tests to your test suite you will see them outputting their results in the console every time you run the Debug version of the app. The full test suite for this example has the following output:

Running…
FixedPointTest::uint32ConstructorTest : OK
FixedPointTest::floatConstructorTest : OK
FixedPointTest::copyConstructorTest : OK
FixedPointTest::assignmentOperatorTest : OK
FixedPointTest::floatValueTest : OK
FixedPointTest::stringValueTest : OK
FixedPointTest::encodedValueTest : OK
OK (7)

The only other case you need to be aware of is when a test breaks and it will tell you what broke and on which line it failed. Below is a contrived example of a failed test I intentionally broke to show a sample.

    1: Running…
    2: FixedPointTest::uint32ConstructorTest : OK
    3: FixedPointTest::floatConstructorTest : OK
    4: FixedPointTest::copyConstructorTest : OK
    5: FixedPointTest::assignmentOperatorTest : assertion
    6: FixedPointTest::floatValueTest : OK
    7: FixedPointTest::stringValueTest : OK
    8: FixedPointTest::encodedValueTest : OK
    9: …/fp/FixedPoint.cpp:399: Assertion
   10: Test name: FixedPointTest::assignmentOperatorTest
   11: equality assertion failed
   12: - Expected: 1
   13: - Actual  : 0
   14:
   15: Failures !!!
   16: Run: 7   Failure total: 1   Failures: 1   Errors: 0

As you can see from this broken test example the error occurred in the assignmentOperatorTest test as shown on line 5 and then on line 9 it tells you which file and line in the test caused the failure. This makes if very easy to figure out which test broke the run and allow you to find it quickly for fixing.

Conclusion

I would like to say that adding unit tests to your new and existing code is a great idea. While it is an investment of time and energy to add all of the tests, in the long run it will save you time. Even if you just start out with one class being tested, it is a good start. I encourage everyone to at least download the sample code and the test framework and to at least try running the sample code. You may not use it today but hopefully you will see the value of it and try to use it in a future project.

Sample Code

This is the Xcode project and sample code used in this article. fp

References

For more in-depth information on topics covered here, please see the following references.

cppunit site: http://sourceforge.net/apps/mediawiki/cppunit/index.php?title=Main_Page
TDD book: Test Driven Development: By Example by Kent Beck [Amazon]
xUnit book: xUnit Test Patterns: Refactoring Test Code by Gerard Meszaros [Amazon]
Unit Testing book: Unit Test Frameworks by Paul Hamill [Amazon]