tst_QAbstractItemView: Fix UB (invalid downcast, member call)

As reported by UBSan:

  tst_qabstractitemview.cpp:336:23: runtime error: member call on address 0x7ffe6fe96e10 which does not point to an object of type 'TestView'
   0x7ffe6fe96e10: note: object is of type 'QListView'
  tst_qabstractitemview.cpp:337:5: runtime error: member call on address 0x7ffe6fe96e10 which does not point to an object of type 'TestView'
   0x7ffe6fe96e10: note: object is of type 'QListView'
  tst_qabstractitemview.cpp:338:23: runtime error: member call on address 0x7ffe6fe96e10 which does not point to an object of type 'TestView'
   0x7ffe6fe96e10: note: object is of type 'QListView'
  [etc ...]

Fix by making the test a friend of QAbstractItemView instead.

Change-Id: I1a08977042296eb34e9dbdb5c0595662dbd2e5ef
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
This commit is contained in:
Marc Mutz 2016-07-28 19:51:53 +03:00
parent a81be85b63
commit 08f38d2214
2 changed files with 76 additions and 175 deletions

View File

@ -39,6 +39,8 @@
#include <QtCore/qitemselectionmodel.h> #include <QtCore/qitemselectionmodel.h>
#include <QtWidgets/qabstractitemdelegate.h> #include <QtWidgets/qabstractitemdelegate.h>
class tst_QAbstractItemView;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -359,6 +361,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_scrollerStateChanged()) Q_PRIVATE_SLOT(d_func(), void _q_scrollerStateChanged())
#endif #endif
friend class ::tst_QAbstractItemView;
friend class QTreeViewPrivate; // needed to compile with MSVC friend class QTreeViewPrivate; // needed to compile with MSVC
friend class QListModeViewBase; friend class QListModeViewBase;
friend class QListViewPrivate; friend class QListViewPrivate;

View File

@ -81,108 +81,6 @@ static inline void moveCursorAway(const QWidget *topLevel)
#endif #endif
} }
class TestView : public QAbstractItemView
{
Q_OBJECT
public:
inline void tst_dataChanged(const QModelIndex &tl, const QModelIndex &br)
{ dataChanged(tl, br); }
inline void tst_setHorizontalStepsPerItem(int steps)
{ setHorizontalStepsPerItem(steps); }
inline int tst_horizontalStepsPerItem() const
{ return horizontalStepsPerItem(); }
inline void tst_setVerticalStepsPerItem(int steps)
{ setVerticalStepsPerItem(steps); }
inline int tst_verticalStepsPerItem() const
{ return verticalStepsPerItem(); }
inline void tst_rowsInserted(const QModelIndex &parent, int start, int end)
{ rowsInserted(parent, start, end); }
inline void tst_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{ rowsAboutToBeRemoved(parent, start, end); }
inline void tst_selectionChanged(const QItemSelection &selected,
const QItemSelection &deselected)
{ selectionChanged(selected, deselected); }
inline void tst_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{ currentChanged(current, previous); }
inline void tst_updateEditorData()
{ updateEditorData(); }
inline void tst_updateEditorGeometries()
{ updateEditorGeometries(); }
inline void tst_updateGeometries()
{ updateGeometries(); }
inline void tst_verticalScrollbarAction(int action)
{ verticalScrollbarAction(action); }
inline void tst_horizontalScrollbarAction(int action)
{ horizontalScrollbarAction(action); }
inline void tst_verticalScrollbarValueChanged(int value)
{ verticalScrollbarValueChanged(value); }
inline void tst_horizontalScrollbarValueChanged(int value)
{ horizontalScrollbarValueChanged(value); }
inline void tst_closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
{ closeEditor(editor, hint); }
inline void tst_commitData(QWidget *editor)
{ commitData(editor); }
inline void tst_editorDestroyed(QObject *editor)
{ editorDestroyed(editor); }
enum tst_CursorAction {
MoveUp = QAbstractItemView::MoveUp,
MoveDown = QAbstractItemView::MoveDown,
MoveLeft = QAbstractItemView::MoveLeft,
MoveRight = QAbstractItemView::MoveRight,
MoveHome = QAbstractItemView::MoveHome,
MoveEnd = QAbstractItemView::MoveEnd,
MovePageUp = QAbstractItemView::MovePageUp,
MovePageDown = QAbstractItemView::MovePageDown,
MoveNext = QAbstractItemView::MoveNext,
MovePrevious = QAbstractItemView::MovePrevious
};
inline QModelIndex tst_moveCursor(tst_CursorAction cursorAction,
Qt::KeyboardModifiers modifiers)
{ return moveCursor(QAbstractItemView::CursorAction(cursorAction), modifiers); }
inline int tst_horizontalOffset() const
{ return horizontalOffset(); }
inline int tst_verticalOffset() const
{ return verticalOffset(); }
inline bool tst_isIndexHidden(const QModelIndex &index) const
{ return isIndexHidden(index); }
inline void tst_setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
{ setSelection(rect, command); }
inline QRegion tst_visualRegionForSelection(const QItemSelection &selection) const
{ return visualRegionForSelection(selection); }
inline QModelIndexList tst_selectedIndexes() const
{ return selectedIndexes(); }
inline bool tst_edit(const QModelIndex &index, EditTrigger trigger, QEvent *event)
{ return edit(index, trigger, event); }
inline QItemSelectionModel::SelectionFlags tst_selectionCommand(const QModelIndex &index,
const QEvent *event = 0) const
{ return selectionCommand(index, event); }
#ifndef QT_NO_DRAGANDDROP
inline void tst_startDrag(Qt::DropActions supportedActions)
{ startDrag(supportedActions); }
#endif
inline QStyleOptionViewItem tst_viewOptions() const
{ return viewOptions(); }
enum tst_State {
NoState = QAbstractItemView::NoState,
DraggingState = QAbstractItemView::DraggingState,
DragSelectingState = QAbstractItemView::DragSelectingState,
EditingState = QAbstractItemView::EditingState,
ExpandingState = QAbstractItemView::ExpandingState,
CollapsingState = QAbstractItemView::CollapsingState
};
inline tst_State tst_state() const
{ return (tst_State)state(); }
inline void tst_setState(tst_State state)
{ setState(QAbstractItemView::State(state)); }
inline void tst_startAutoScroll()
{ startAutoScroll(); }
inline void tst_stopAutoScroll()
{ stopAutoScroll(); }
inline void tst_doAutoScroll()
{ doAutoScroll(); }
};
class GeometriesTestView : public QTableView class GeometriesTestView : public QTableView
{ {
Q_OBJECT Q_OBJECT
@ -198,7 +96,7 @@ class tst_QAbstractItemView : public QObject
Q_OBJECT Q_OBJECT
public: public:
void basic_tests(TestView *view); void basic_tests(QAbstractItemView *view);
private slots: private slots:
void initTestCase(); void initTestCase();
@ -282,7 +180,7 @@ public:
void tst_QAbstractItemView::getSetCheck() void tst_QAbstractItemView::getSetCheck()
{ {
QListView view; QListView view;
TestView *obj1 = reinterpret_cast<TestView*>(&view); QAbstractItemView *obj1 = &view;
// QAbstractItemDelegate * QAbstractItemView::itemDelegate() // QAbstractItemDelegate * QAbstractItemView::itemDelegate()
// void QAbstractItemView::setItemDelegate(QAbstractItemDelegate *) // void QAbstractItemView::setItemDelegate(QAbstractItemDelegate *)
MyAbstractItemDelegate *var1 = new MyAbstractItemDelegate; MyAbstractItemDelegate *var1 = new MyAbstractItemDelegate;
@ -333,18 +231,18 @@ void tst_QAbstractItemView::getSetCheck()
// State QAbstractItemView::state() // State QAbstractItemView::state()
// void QAbstractItemView::setState(State) // void QAbstractItemView::setState(State)
obj1->tst_setState(TestView::tst_State(TestView::NoState)); obj1->setState(QAbstractItemView::NoState);
QCOMPARE(TestView::tst_State(TestView::NoState), obj1->tst_state()); QCOMPARE(QAbstractItemView::NoState, obj1->state());
obj1->tst_setState(TestView::tst_State(TestView::DraggingState)); obj1->setState(QAbstractItemView::DraggingState);
QCOMPARE(TestView::tst_State(TestView::DraggingState), obj1->tst_state()); QCOMPARE(QAbstractItemView::DraggingState, obj1->state());
obj1->tst_setState(TestView::tst_State(TestView::DragSelectingState)); obj1->setState(QAbstractItemView::DragSelectingState);
QCOMPARE(TestView::tst_State(TestView::DragSelectingState), obj1->tst_state()); QCOMPARE(QAbstractItemView::DragSelectingState, obj1->state());
obj1->tst_setState(TestView::tst_State(TestView::EditingState)); obj1->setState(QAbstractItemView::EditingState);
QCOMPARE(TestView::tst_State(TestView::EditingState), obj1->tst_state()); QCOMPARE(QAbstractItemView::EditingState, obj1->state());
obj1->tst_setState(TestView::tst_State(TestView::ExpandingState)); obj1->setState(QAbstractItemView::ExpandingState);
QCOMPARE(TestView::tst_State(TestView::ExpandingState), obj1->tst_state()); QCOMPARE(QAbstractItemView::ExpandingState, obj1->state());
obj1->tst_setState(TestView::tst_State(TestView::CollapsingState)); obj1->setState(QAbstractItemView::CollapsingState);
QCOMPARE(TestView::tst_State(TestView::CollapsingState), obj1->tst_state()); QCOMPARE(QAbstractItemView::CollapsingState, obj1->state());
// QWidget QAbstractScrollArea::viewport() // QWidget QAbstractScrollArea::viewport()
// void setViewport(QWidget*) // void setViewport(QWidget*)
@ -405,7 +303,7 @@ void tst_QAbstractItemView::emptyModels()
QVERIFY(!view->selectionModel()); QVERIFY(!view->selectionModel());
//QVERIFY(view->itemDelegate() != 0); //QVERIFY(view->itemDelegate() != 0);
basic_tests(reinterpret_cast<TestView*>(view.data())); basic_tests(view.data());
} }
void tst_QAbstractItemView::setModel_data() void tst_QAbstractItemView::setModel_data()
@ -442,10 +340,10 @@ void tst_QAbstractItemView::setModel()
QStandardItemModel model(20,20); QStandardItemModel model(20,20);
view->setModel(0); view->setModel(0);
view->setModel(&model); view->setModel(&model);
basic_tests(reinterpret_cast<TestView*>(view.data())); basic_tests(view.data());
} }
void tst_QAbstractItemView::basic_tests(TestView *view) void tst_QAbstractItemView::basic_tests(QAbstractItemView *view)
{ {
// setSelectionModel // setSelectionModel
// Will assert as it should // Will assert as it should
@ -570,73 +468,73 @@ void tst_QAbstractItemView::basic_tests(TestView *view)
view->setCurrentIndex(QModelIndex()); view->setCurrentIndex(QModelIndex());
// protected methods // protected methods
view->tst_dataChanged(QModelIndex(), QModelIndex()); view->dataChanged(QModelIndex(), QModelIndex());
view->tst_rowsInserted(QModelIndex(), -1, -1); view->rowsInserted(QModelIndex(), -1, -1);
view->tst_rowsAboutToBeRemoved(QModelIndex(), -1, -1); view->rowsAboutToBeRemoved(QModelIndex(), -1, -1);
view->tst_selectionChanged(QItemSelection(), QItemSelection()); view->selectionChanged(QItemSelection(), QItemSelection());
if (view->model()){ if (view->model()){
view->tst_currentChanged(QModelIndex(), QModelIndex()); view->currentChanged(QModelIndex(), QModelIndex());
view->tst_currentChanged(QModelIndex(), view->model()->index(0,0)); view->currentChanged(QModelIndex(), view->model()->index(0,0));
} }
view->tst_updateEditorData(); view->updateEditorData();
view->tst_updateEditorGeometries(); view->updateEditorGeometries();
view->tst_updateGeometries(); view->updateGeometries();
view->tst_verticalScrollbarAction(QAbstractSlider::SliderSingleStepAdd); view->verticalScrollbarAction(QAbstractSlider::SliderSingleStepAdd);
view->tst_horizontalScrollbarAction(QAbstractSlider::SliderSingleStepAdd); view->horizontalScrollbarAction(QAbstractSlider::SliderSingleStepAdd);
view->tst_verticalScrollbarValueChanged(10); view->verticalScrollbarValueChanged(10);
view->tst_horizontalScrollbarValueChanged(10); view->horizontalScrollbarValueChanged(10);
view->tst_closeEditor(0, QAbstractItemDelegate::NoHint); view->closeEditor(0, QAbstractItemDelegate::NoHint);
view->tst_commitData(0); view->commitData(0);
view->tst_editorDestroyed(0); view->editorDestroyed(0);
view->tst_setHorizontalStepsPerItem(2); view->setHorizontalStepsPerItem(2);
view->tst_horizontalStepsPerItem(); view->horizontalStepsPerItem();
view->tst_setVerticalStepsPerItem(2); view->setVerticalStepsPerItem(2);
view->tst_verticalStepsPerItem(); view->verticalStepsPerItem();
// Will assert as it should // Will assert as it should
// view->setIndexWidget(QModelIndex(), 0); // view->setIndexWidget(QModelIndex(), 0);
view->tst_moveCursor(TestView::MoveUp, Qt::NoModifier); view->moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier);
view->tst_horizontalOffset(); view->horizontalOffset();
view->tst_verticalOffset(); view->verticalOffset();
// view->tst_isIndexHidden(QModelIndex()); // will (correctly) assert // view->isIndexHidden(QModelIndex()); // will (correctly) assert
if(view->model()) if(view->model())
view->tst_isIndexHidden(view->model()->index(0,0)); view->isIndexHidden(view->model()->index(0,0));
view->tst_setSelection(QRect(0, 0, 10, 10), QItemSelectionModel::ClearAndSelect); view->setSelection(QRect(0, 0, 10, 10), QItemSelectionModel::ClearAndSelect);
view->tst_setSelection(QRect(-1, -1, -1, -1), QItemSelectionModel::ClearAndSelect); view->setSelection(QRect(-1, -1, -1, -1), QItemSelectionModel::ClearAndSelect);
view->tst_visualRegionForSelection(QItemSelection()); view->visualRegionForSelection(QItemSelection());
view->tst_selectedIndexes(); view->selectedIndexes();
view->tst_edit(QModelIndex(), QAbstractItemView::NoEditTriggers, 0); view->edit(QModelIndex(), QAbstractItemView::NoEditTriggers, 0);
view->tst_selectionCommand(QModelIndex(), 0); view->selectionCommand(QModelIndex(), 0);
#ifndef QT_NO_DRAGANDDROP #ifndef QT_NO_DRAGANDDROP
if (!view->model()) if (!view->model())
view->tst_startDrag(Qt::CopyAction); view->startDrag(Qt::CopyAction);
view->tst_viewOptions(); view->viewOptions();
view->tst_setState(TestView::NoState); view->setState(QAbstractItemView::NoState);
QVERIFY(view->tst_state()==TestView::NoState); QVERIFY(view->state()==QAbstractItemView::NoState);
view->tst_setState(TestView::DraggingState); view->setState(QAbstractItemView::DraggingState);
QVERIFY(view->tst_state()==TestView::DraggingState); QVERIFY(view->state()==QAbstractItemView::DraggingState);
view->tst_setState(TestView::DragSelectingState); view->setState(QAbstractItemView::DragSelectingState);
QVERIFY(view->tst_state()==TestView::DragSelectingState); QVERIFY(view->state()==QAbstractItemView::DragSelectingState);
view->tst_setState(TestView::EditingState); view->setState(QAbstractItemView::EditingState);
QVERIFY(view->tst_state()==TestView::EditingState); QVERIFY(view->state()==QAbstractItemView::EditingState);
view->tst_setState(TestView::ExpandingState); view->setState(QAbstractItemView::ExpandingState);
QVERIFY(view->tst_state()==TestView::ExpandingState); QVERIFY(view->state()==QAbstractItemView::ExpandingState);
view->tst_setState(TestView::CollapsingState); view->setState(QAbstractItemView::CollapsingState);
QVERIFY(view->tst_state()==TestView::CollapsingState); QVERIFY(view->state()==QAbstractItemView::CollapsingState);
#endif #endif
view->tst_startAutoScroll(); view->startAutoScroll();
view->tst_stopAutoScroll(); view->stopAutoScroll();
view->tst_doAutoScroll(); view->doAutoScroll();
// testing mouseFoo and key functions // testing mouseFoo and key functions
// QTest::mousePress(view, Qt::LeftButton, Qt::NoModifier, QPoint(0,0)); // QTest::mousePress(view, Qt::LeftButton, Qt::NoModifier, QPoint(0,0));
@ -771,11 +669,11 @@ void tst_QAbstractItemView::selectAll()
QTableView view; QTableView view;
view.setModel(&model); view.setModel(&model);
TestView *tst_view = (TestView*)&view; QAbstractItemView *tst_view = &view;
QCOMPARE(tst_view->tst_selectedIndexes().count(), 0); QCOMPARE(tst_view->selectedIndexes().count(), 0);
view.selectAll(); view.selectAll();
QCOMPARE(tst_view->tst_selectedIndexes().count(), 4*4); QCOMPARE(tst_view->selectedIndexes().count(), 4*4);
} }
void tst_QAbstractItemView::ctrlA() void tst_QAbstractItemView::ctrlA()
@ -784,11 +682,11 @@ void tst_QAbstractItemView::ctrlA()
QTableView view; QTableView view;
view.setModel(&model); view.setModel(&model);
TestView *tst_view = (TestView*)&view; QAbstractItemView *tst_view = &view;
QCOMPARE(tst_view->tst_selectedIndexes().count(), 0); QCOMPARE(tst_view->selectedIndexes().count(), 0);
QTest::keyClick(&view, Qt::Key_A, Qt::ControlModifier); QTest::keyClick(&view, Qt::Key_A, Qt::ControlModifier);
QCOMPARE(tst_view->tst_selectedIndexes().count(), 4*4); QCOMPARE(tst_view->selectedIndexes().count(), 4*4);
} }
void tst_QAbstractItemView::persistentEditorFocus() void tst_QAbstractItemView::persistentEditorFocus()
@ -1604,9 +1502,9 @@ void tst_QAbstractItemView::testDelegateDestroyEditor()
MyAbstractItemDelegate delegate; MyAbstractItemDelegate delegate;
table.setItemDelegate(&delegate); table.setItemDelegate(&delegate);
table.edit(table.model()->index(1, 1)); table.edit(table.model()->index(1, 1));
TestView *tv = reinterpret_cast<TestView*>(&table); QAbstractItemView *tv = &table;
QVERIFY(!delegate.calledVirtualDtor); QVERIFY(!delegate.calledVirtualDtor);
tv->tst_closeEditor(delegate.openedEditor, QAbstractItemDelegate::NoHint); tv->closeEditor(delegate.openedEditor, QAbstractItemDelegate::NoHint);
QVERIFY(delegate.calledVirtualDtor); QVERIFY(delegate.calledVirtualDtor);
} }