should DRY apply to unit tests as much as production code
As Cygal pointed out, keeping things DRY is a very good thing and I clearly agree with that. At the same time I did not want to just simply blame the tool for its limitations or to have to use a debugger when one really shouldn't have to be used.

So here's an alternative. 

We have our own CppUnit library on top of CppUnit where we defined a whole bunch of helpers not available in the framework (things like CPPUNIT_ASSERT_IN_RANGE( min, max, value ) macro).
So I added this macro to our library:

#define CPPUNIT_CALL( expression )                                  \
    do {                                                            \
        try {                                                       \
            expression;                                             \
        } catch( ::CppUnit::Exception&amp; e ) {                        \
            _RethrowCppUnitAssert( e, CPPUNIT_SOURCELINE() );       \
        }                                                           \
    } while ( false )


and the rethrow function is defined as:

inline void _RethrowCppUnitAssert( CPPUNIT_NS::Exception&amp;           e,
                                   const CPPUNIT_NS::SourceLine&amp;    outerLocation ) {
    CPPUNIT_NS::Message     msg( e.message() );
    char                    location[4096];
    sprintf_s( location, "From: %s(%d)",
               outerLocation.fileName().c_str(), outerLocation.lineNumber() );
    msg.addDetail( location );
    CPPUNIT_NS::Asserter::fail( msg, e.sourceLine() );
}


So now the actual unit test code looks like this:

...

CPPUNIT_CALL( SendNextPacket( 0, 1, 2 ) );

CPPUNIT_CALL( SendNextPacket( 1, 2, 2 ) );

...


Works like a charm and everything is DRY as it should be.  If anything inside those calls throws an assertion, that assertion will be intercepted, source file/line number of the CPPUNIT_CALL will be added to message details, and the whole thing will be rethrown.

(*) - p.s. I'm not sure what's up with do {... } while( false ) loop in there. I modeled this macro after CPPUNIT_ASSERT_NO_THROW_MESSAGE and that was in there, so I figured somebody at some point must have had a good reason.