qabstractitemmodeltester and editabletreemodel test failures
-
In qabstractitemmodeltester, the following test case is run when used on a tree model.
qabstractitemmodeltester - SNIPPET 1:// Common error test #3, the second column should NOT have the same children // as the first column in a row. // Usually the second column shouldn't have children. if (model->hasIndex(0, 1)) { QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); MODELTESTER_VERIFY(topIndex1.isValid()); if (model->hasChildren(topIndex) && model->hasChildren(topIndex1)) { QModelIndex childIndex = model->index(0, 0, topIndex); MODELTESTER_VERIFY(childIndex.isValid()); QModelIndex childIndex1 = model->index(0, 0, topIndex1); MODELTESTER_VERIFY(childIndex1.isValid()); MODELTESTER_VERIFY(childIndex != childIndex1); } }
As per the editableTreeModel example, if the column is anything other than 0, the model returns an invalid index in the index method as shown below:
editableTreeModel - SNIPPET 2:if (parent.isValid() && parent.column() != 0) return QModelIndex();
Going back to the tester code above:
In SNIPPET 1, childIndex1 will be invalid, because SNIPPET 2 returns an invalid index for all columns other than zero.
If I remove the column 0 check in SNIPPET 2, then in SNIPPET 1 it fails on the last line because childIndex IS EQUAL TO childIndex.What is the right implementation here? Only when I return an index for all columns, the code seems to work correctly, but the model tester fails..
-
So what's your actual implementation? The tester is correct. If the model says it has an index at index(x/y) then it should return a unique and valid one.
-
@Christian-Ehrlicher Would it be possible for you to look at the editabletreemodel example that ships with QtCreator? When the model tester is applied to the example, it will fail with the failures above.
Here are the steps to apply the modeltester to the editabletreemodel example:
1)after the editabletreemodel example has been setup in QtCreator, open the editabletreemodel.pro file. Add "testlib" to the this line "QT += widgets ".
2)Open mainwindow.cpp and add ```
"#include <QAbstractItemModelTester>"
3) Add the line shown below//The line below is in the example TreeModel *model = new TreeModel(headers, file.readAll()); //Add the line below new QAbstractItemModelTester(model, QAbstractItemModelTester::FailureReportingMode::Fatal, this);
4)Run the example and you will see the above failures.
Thanks
-
@JohnGa Thx, the columnCount() function is wrong (as already guessed in my first post). It must be
int TreeModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : rootItem->columnCount();
}int TreeModel::columnCount(const QModelIndex &parent) const { return (!parent.isValid() || parent.column() == 0) ? rootItem->columnCount() : 0; }
-> only the first column has sub-columns, not the second (which would look somehow strange)
It has no real-world impact in the Qt since the current implementation does first check for other things but is wrong when you want to catch all cases - the model tester is very picky here :). Will create a patch for it, thx.
-
Hi @Christian-Ehrlicher ,
The topic is old, but my question exactly match the context of it so I decided to ask it here instead of creating a new topic.JohnGa gave a snippet of test case and my model fails at the last line:
MODELTESTER_VERIFY(childIndex != childIndex1);
I made it based on this example that hase following implementation of
index()
method:QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); TreeItem *parentItem; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast<TreeItem*>(parent.internalPointer()); TreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); return QModelIndex(); }
As I see the test do the following:
Creates first top indexmodel->index(0, 0, QModelIndex())
Creates second top indexmodel->index(0, 1, QModelIndex())
So three are 2 indices (0, 0, <void>) and (0, 1, <void) but both have the same value ofinternalPointer
aschildItem
depends onrow
only.
As next step the test makes calls
model->index(0, 0, topIndex)
andmodel->index(0, 0, topIndex1)
with different parent indices, but - both of them have the same internal pointer. So the callparentItem->child(row)
will provide the same value of thechildItem
.
And finally thisreturn createIndex(row, column, childItem)
will give two identical indices(0, 0, childItem)
that will fail the test.But I don't fully understand what is wrong with all this story.
My guess is thatindex()
method should addchildItem
data only for column 0. But thenparent()
method of the example will fail. So I assume it should be changed to be more inteligente looking intointernalPointer()
of the first column. Is it right understanding or I miss something here? -
@DavidFaure knows maybe more on what's wrong here.
-
@Christian-Ehrlicher , I think I found how it works in the example. There is an additional restriction in
rowCount()
method of the model:int TreeModel::rowCount(const QModelIndex &parent) const { if (parent.column() > 0) return 0;
I.e. it simply reports no rows if asked for any column other than the left one. As result I assume there are no queries done for child indices outside the left column.
Probably it is right... but not obvious...
-
@StarterKit Yes, that "parent.column() > 0" check is necessary in the rowCount method. That is how I implemented it in all my TreeModels.
I do:
if(parent.isValid() && parent.column() > 0) return 0;Hope that helps.