subcommanderblog.wordpress.com

Subcommander Development Blog

Posts Tagged ‘cppunit

creating a custom QAbstractProxyModel

leave a comment »

Part I, getting started.

I’m currently implementing a custom QAbstractProxyModel for subcommanders working copy view. The first reason is that I want to improve the status filtering in subcommander which is not possible with the current code based on QSortFilterProxyModel. The second reason is to get full control of sorting. My attempt to add sorting to the current code failed in such a frustrating way that I don’t want to track down the problem and simply start again with fresh code that I will be able to debug :-)

Before starting with the interesting stuff (filtering and sorting) I will create a simple proxy model which does not do any sorting or filtering. It will just pass through the original (tree) model without modification. Getting this running is a big step toward the final goal.

Important for a proxy model is that is emits all the signals the source or original model emits. To achieve this the proxy model must connect to all (interesting) signals from the source model and emit the same signals with proper information from the proxy model.

I have created a test checking two signals emitted from the proxy model. To count the emitted signals I’m using a SignalTarget class which connects to the proxy signals and checks if the signals were emitted. Since the verification (CPPUNIT_ASSERT) is done inside this class it is a mock. It replaces the widget (in my case a QTreeView) that will be connected to the proxy model in the production code.

class  SignalTarget : public  QObject
{
  Q_OBJECT; 

public:
  SignalTarget( QObject* src );
  ~SignalTarget(); 

  virtual void verify(); 

public  slots:
  void modelAboutToBeReset();
  void modelReset(); 

private:
  QObject* _src; 

protected:
  int  _hitModelAboutToBeReset;
  int  _hitModelReset;
};
 

The idea is to count the emitted signals and check the results in the verify method. It is not yet complete. It does handle only a subset of the signals we need to check. I have declared verify as virtual so it easy to re-implement and do the actual check. The default implementation is empty. The constructor and destructor will simply connect and disconnect all signals the we need to handle in the proxy model. The slot method names correspond to the model signals.

A test method can now use SignalTarget to check the signals from the proxy model. You can see the complete test below. _proxy is the proxy model and gets created by the test setup. The reset() method triggers both signals. Because I care only for the reset signals I have overwritten verify() to simply check their call counts.

void WcViewItemProxyModelTest::emitsModelResetSignals()
{
  class Target : public SignalTarget {
  public:
    Target( QObject* src ) : SignalTarget(src) {
    }

    void verify() {
      CPPUNIT_ASSERT_EQUAL( 1, _hitModelAboutToBeReset );
      CPPUNIT_ASSERT_EQUAL( 1, _hitModelReset );
    }
  };

  Target target(_proxy);
  _proxy->reset();
  target.verify();
}
 

This is similar to the signal testing I mentioned in my last article. The only difference is that originally I also tried to check QModelIndex indices which didn’t work to well. The problem was creating valid expected indices to assert on. I tried to remember indices from previous signals and build new expected indices (sibling, parent,child) from them. Unfortunately this approach got complicated rather fast so you couldn’t follow the code anymore.

To get the test running I simply connect the soure model modelReset signal to the srcModelReset() slot in the proxy model and call reset(). reset() will emit both signals the test wants to see.

void WcViewItemProxyModel::srcModelReset()
{
  reset();
}
 

Very simple so far, but one has to start somewhere. :-) I guess it will get more interesting for some of the other signals the proxy model has to handle. More on this in the next article.

Advertisements

Written by hauner

Sunday, 8 February, 2009 at 19:07

Posted in subcommander

Tagged with , ,

CPPUNIT_ASSERT_EQUAL and custom data types

with 2 comments

To write more bookmark specific settings information to the configuration file I rewrote and simplified the code that reads subcommanders project settings.  I added a few tests for the new refactored code and  used a ccpunit feature  I had discovered a while ago: assertion traits.

If you have never heard of it read on and read what an assertion trait is and how it can help in handling custom data types in cppunit tests.

I’m using a custom build string class in the subversion related code to handle utf-8 encoded strings (sc::String) in Subcommander.

This works well, but there was an issue when trying to use Strings in test code:

CPPUNIT_ASSERT_EQUAL( expected, actual );

The assertion above “crashed” when printing its error message when one of the two strings was an empty string. It crashed in stl output stream code that was called by the following code inside cppunit (from TestAssert.h):

template <class T> struct assertion_traits
{
    static bool equal( const T& x, const T& y )
    {
        return x == y;
    }

    static std::string toString( const T& x )
    {
        OStringStream ost;
        ost << x;
        return ost.str();
    }
};

Very interesting code!  CPPUNIT_ASSERT_EQUAL(a,b) calls the equal method in this template to compare its two parameters a and b and calls the toString method to print the failure message if the assertion fails.

By using a template feature that’s called template specialization we can re-implement the template for a specific data type. If the compiler finds a specialization for a specific type, in my case my custom String class, it  uses the specialization for this type instead of  instantiating the generic T template for the type.

Here is the “new” code for my String class. It is nearly the same as the generic code but handles the empty string case in toString (there is also an example in cppunit’s  TestAssert.h file):

template<> struct CPPUNIT_NS::assertion_traits<sc::String>
{
  static bool equal( const sc::String& x, const sc::String& y )
  {
    return x == y;
  }

  static std::string toString( const sc::String& x )
  {
    std::string text = '"' + (!x.isEmpty() ? std::string(x) : "<null>") + '"';
    OStringStream ost;
    ost << text;
    return ost.str();
  }
};

The interesting thing about specializing the generic assertion traits is that you can add support for any custom data type to CPPUNIT_ASSERT_EQUAL with just a few lines of code.

This is even a documented feature. Surprisingly a google search on assertion_traits delivers only about 220 hits.

Either nobody knows about this feature or it just so obvious that nobody cares to write about it :-)

Written by hauner

Saturday, 10 January, 2009 at 18:14

Posted in subcommander

Tagged with ,

Creating a testrunner..

leave a comment »

I think i finally found the easiest way to run and organize cppunit tests. Subcommander is split up into several libs and two binaries and a testrunner binary:

subcommander  (binary)
submerge      (binary)
sublib        (static lib)
util          (static lib)
svn           (static lib)
testrunner    (binary)

So far the testrunner contained the tests and linked with all the libs. The issue with this layout is that if i wanted to write a test for code in the subcommander binary and not in a library i had to add the source files to the testrunner. This gets combersome, especially with QObject based classes which also needs their moc files.

My new layout works upside down. I moved the test classes into its own libraries, …

subcommander       (binary)
subcommander-test  (static lib)
...
sublib             (static lib)
sublib-test        (static lib)
...

.. moved the testrunner code into the subcommander binary and link the binary with the test libraries. Running subcommander  with the -test command line option will run the tests. Without it, it will run the normal production code.

The test code is a lot easier to handle this way. The only drawback is that the production code now also contains all the test code… but maybe that’s not really a drawback? Everyone can run the tests now.

So far this works really good.  :-)

Written by hauner

Saturday, 13 December, 2008 at 12:16

Posted in subcommander

Tagged with ,