Now that Grantlee is out the door I’ve turned my attention back to Akonadi for the moment. My role in Akonadi is that of Models writer and noise maker, primarily the former at the moment.
In Akonadi, we have a relatively simple to use model called EntityTreeModel which represents a tree of collections PIM items. That might mean emails, or contacts etc. Applications have needs like ‘show me all contacts in all child collections below this collection’. The functionality is similar to the current behaviour of Akregator for example. To satisfy that we have a generic proxy model DescendentEntitiesProxyModel, which is based on the older FlatCollectionProxyModel in Akonadi.
There is also the use-case of ‘Show me content of this collection in that view’. This behaviour is similar to the behaviour of many other PIM applications. Select a folder in the collection tree in KMail and you see the emails in that collection in the email list on the right. The current KMail implementation works only with one selected collection at a time however. For a more generic solution to the problem, we now also have SelectionProxyModel, which uses a QItemSelectionModel which manages filtering of its source model. Whatever is selected in one view will be shown on its own in another view- even for non-contiguous or block selections. This takes away any need to mess around with mapToSource, mapFromSource, and setRootIndex.
Already I can think of two other proxy models that I will probably be writing over the next few months for Akonadi, so I needed a good test suite for them. While writing the above models, I was writing small test cases for various issues to reproduce various Qt bugs. Eventually, I wrote an easily manipulatable model- DynamicTreeModel which uses the Command Pattern to create any kind of tree I wanted to test. Using that I can insert rows and subtrees, remove rows, move rows, and change row data by creating lists of Command objects and executing them.
For a while I was using DynamicTreeModel together with ModelTest as my proxymodel unit test suite. The limitations of this is that ModelTest only checks a model under test for consistency, not for correctness. For example, if a proxy model receives a dataChanged signal from its sourceModel, but doesn’t react to it or emit any signals to the outside world about it, ModelTest still passes. That’s fine in some cases, but really you need some way to specify whether a signal should be emitted or not, and that the correct signal is emitted. Lastly, you need to assure that any QPersistentModelIndexes from the model are updated to their correct values after reacting to the signal.
Instead of writing tests to handle all that once for each proxy model, I decided to write it only once. Now I have a ProxyModelTest class, which is half a unit test case. It manipulates a DynamicTreeModel into emitting a wide range of signals, each of which the proxy model will have to have some reaction to (even if it’s ‘Do nothing’). The other half of each unit test specifies what signals the proxy should emit for each signal that it recieves from ProxyModelTest which QPersistentModelIndexes should be updated, and what they should be updated to.
I’ve already written some tests for DescendantEntitesProxyModel and SelectionProxyModel using it.
My solution so far is quite incomplete, but only just started.
How do you test proxy models? Any other ideas?
January 6, 2010 at 12:17 pm |
[…] have written before about testing of proxy models, but things have progressed greatly since then. Aside from creating a unit test suite, I also […]
March 22, 2010 at 1:37 pm |
[…] first attempt at doing so resulted in a unit test suite which commanded a source model through a sequence of […]