Android: Add Java QtAbstractItemModel auto test

QtAbstractItemModel used to be a part of QtDeclarative
and is being moved to QtCore based on API reviews.

Pick-to: 6.8
Task-number: QTBUG-126976
Task-number: QTBUG-127089
Change-Id: I28a921a113ec4f3ad298bf55b40aea334e7721cf
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
Soheil Armin 2024-07-17 10:23:02 +03:00
parent 55fc15ce82
commit baed8a24d0
4 changed files with 341 additions and 0 deletions

View File

@ -4,6 +4,7 @@
if(ANDROID)
add_subdirectory(android)
add_subdirectory(android_appless)
add_subdirectory(androiditemmodel)
endif()
if(WIN32)
add_subdirectory(windows)

View File

@ -0,0 +1,24 @@
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_androiditemmodel Test:
#####################################################################
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_androiditemmodel LANGUAGES CXX)
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
qt_internal_add_test(tst_androiditemmodel
SOURCES
tst_androiditemmodel.cpp
LIBRARIES
Qt::Gui
Qt::CorePrivate
)
set_property(TARGET tst_androiditemmodel APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/testdata
)

View File

@ -0,0 +1,134 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
package org.qtproject.qt.android.tests;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.qtproject.qt.android.QtAbstractItemModel;
import org.qtproject.qt.android.QtModelIndex;
public class TestModel extends QtAbstractItemModel
{
int m_rows = 0;
int m_cols = 0;
@Override
public int columnCount(QtModelIndex parent)
{
return parent.isValid() ? 0 : m_cols;
}
@Override
public Object data(QtModelIndex index, int role)
{
int r = index.row();
int c = index.column();
if (r < 0 || c < 0 || c > m_cols || r > m_rows)
return null;
switch (role) {
case 0:
return String.format("r%d/c%d", r, c);
case 1:
return new Boolean(((r + c) % 2) == 0);
case 2:
return new Integer((c << 8) + r);
case 3:
return new Double((r + 1.0) / (c + 1.0));
case 4:
return new Long((c << 8) * (r << 8));
default:
return null;
}
}
@Override
public QtModelIndex index(int row, int column, QtModelIndex parent)
{
return hasIndex(row, column, parent) ? createIndex(row, column, 0) : new QtModelIndex();
}
@Override
public QtModelIndex parent(QtModelIndex qtModelIndex)
{
return new QtModelIndex();
}
@Override
public int rowCount(QtModelIndex parent)
{
return parent.isValid() ? 0 : m_rows;
}
@Override
public HashMap<Integer, String> roleNames()
{
final HashMap<Integer, String> roles = new HashMap<Integer, String>();
roles.put(0, "stringRole");
roles.put(1, "booleanRole");
roles.put(2, "integerRole");
roles.put(3, "doubleRole");
roles.put(4, "longRole");
return roles;
}
@Override
public boolean canFetchMore(QtModelIndex parent)
{
return !parent.isValid() && (m_rows < 30);
}
@Override
public void fetchMore(QtModelIndex parent)
{
if (!canFetchMore(parent))
return;
int toAdd = Math.min(10, 30 - rowCount(parent));
beginInsertRows(new QtModelIndex(), m_rows, m_rows + toAdd - 1);
m_rows += toAdd;
endInsertRows();
}
public void addRow()
{
beginInsertRows(new QtModelIndex(), m_rows, m_rows);
m_rows++;
endInsertRows();
}
public void removeRow()
{
if (m_rows == 0)
return;
beginRemoveRows(new QtModelIndex(), 0, 0);
m_rows--;
endRemoveRows();
}
public void addCol()
{
beginInsertColumns(new QtModelIndex(), m_cols, m_cols);
m_cols++;
endInsertColumns();
}
public void removeCol()
{
if (m_cols == 0)
return;
beginRemoveColumns(new QtModelIndex(), 0, 0);
m_cols--;
endRemoveColumns();
}
public void reset()
{
beginResetModel();
m_rows = 0;
m_cols = 0;
endResetModel();
}
}

View File

@ -0,0 +1,182 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QTest>
#include <QtCore/private/qandroiditemmodelproxy_p.h>
#include <QtCore/private/qandroidmodelindexproxy_p.h>
#include <QtCore/private/qandroidtypes_p.h>
#include <QGuiApplication>
#include <QtCore/qabstractitemmodel.h>
#include <QtCore/qjniobject.h>
#include <QtCore/qjnitypes.h>
#include <QtCore/qstring.h>
using namespace Qt::Literals;
Q_DECLARE_JNI_CLASS(JTestModel, "org/qtproject/qt/android/tests/TestModel")
class tst_AndroidItemModel : public QObject
{
Q_OBJECT
JTestModel jModel;
QAbstractItemModel *qProxy;
void resetModel();
private slots:
void initTestCase();
void cleanup();
void addRow();
void addColumn();
void removeRow();
void removeColumn();
void roleNames();
void fetchMore();
void hasIndex();
void data();
};
void tst_AndroidItemModel::initTestCase()
{
QVERIFY(jModel.isValid());
qProxy = QAndroidItemModelProxy::createNativeProxy(jModel);
QVERIFY(qProxy);
}
void tst_AndroidItemModel::cleanup()
{
resetModel();
}
void tst_AndroidItemModel::addRow()
{
const int rowsBefore = qProxy->rowCount();
jModel.callMethod<void>("addRow");
QCOMPARE_EQ(qProxy->rowCount(), rowsBefore + 1);
}
void tst_AndroidItemModel::addColumn()
{
const int columnsBefore = qProxy->columnCount();
jModel.callMethod<void>("addCol");
QCOMPARE_EQ(qProxy->columnCount(), columnsBefore + 1);
}
void tst_AndroidItemModel::removeRow()
{
jModel.callMethod<void>("addRow");
jModel.callMethod<void>("addRow");
QCOMPARE_EQ(qProxy->rowCount(), 2);
jModel.callMethod<void>("removeRow");
QCOMPARE_EQ(qProxy->rowCount(), 1);
jModel.callMethod<void>("removeRow");
QCOMPARE_EQ(qProxy->rowCount(), 0);
}
void tst_AndroidItemModel::removeColumn()
{
jModel.callMethod<void>("addCol");
jModel.callMethod<void>("addCol");
QCOMPARE_EQ(qProxy->columnCount(), 2);
jModel.callMethod<void>("removeCol");
QCOMPARE_EQ(qProxy->columnCount(), 1);
jModel.callMethod<void>("removeCol");
QCOMPARE_EQ(qProxy->columnCount(), 0);
}
void tst_AndroidItemModel::roleNames()
{
const static QHash<int, QByteArray> expectedRoles = { { 0, "stringRole" },
{ 1, "booleanRole" },
{ 2, "integerRole" },
{ 3, "doubleRole" },
{ 4, "longRole" } };
QCOMPARE(qProxy->roleNames(), expectedRoles);
}
void tst_AndroidItemModel::fetchMore()
{
// In the Java TestModel :
// canFetchMore() returns true when row count is less than 30
// fetchMore() adds 10 rows at most, or the remaining until row count is 30
QVERIFY(qProxy->canFetchMore(QModelIndex()));
qProxy->fetchMore(QModelIndex());
QCOMPARE_EQ(qProxy->rowCount(), 10);
QVERIFY(qProxy->canFetchMore(QModelIndex()));
qProxy->fetchMore(QModelIndex());
QCOMPARE_EQ(qProxy->rowCount(), 20);
jModel.callMethod<void>("addRow");
QVERIFY(qProxy->canFetchMore(QModelIndex()));
qProxy->fetchMore(QModelIndex());
QCOMPARE_EQ(qProxy->rowCount(), 30);
QVERIFY(!qProxy->canFetchMore(QModelIndex()));
}
void tst_AndroidItemModel::hasIndex()
{
// fetchMore() adds 10 rows
qProxy->fetchMore(QModelIndex());
jModel.callMethod<void>("addCol");
jModel.callMethod<void>("addCol");
for (int r = 0; r < 10; ++r) {
for (int c = 0; c < 2; ++c) {
QVERIFY(qProxy->hasIndex(r, c));
}
}
}
void tst_AndroidItemModel::data()
{
const static QHash<int, QMetaType::Type> roleToType = { { 0, QMetaType::QString },
{ 1, QMetaType::Bool },
{ 2, QMetaType::Int },
{ 3, QMetaType::Double },
{ 4, QMetaType::Long } };
QVERIFY(qProxy->canFetchMore(QModelIndex()));
qProxy->fetchMore(QModelIndex());
QCOMPARE_EQ(qProxy->rowCount(), 10);
jModel.callMethod<void>("addCol");
jModel.callMethod<void>("addCol");
jModel.callMethod<void>("addCol");
for (int r = 0; r < 10; ++r) {
for (int c = 0; c < 3; ++c) {
QModelIndex index = qProxy->index(r, c);
for (int role : roleToType.keys()) {
const QVariant data = qProxy->data(index, role);
QCOMPARE_EQ(data.typeId(), roleToType[role]);
switch (role) {
case 0:
QCOMPARE(data.toString(),
"r%1/c%2"_L1.arg(QString::number(r), QString::number(c)));
break;
case 1:
QCOMPARE(data.toBool(), ((r + c) % 2) == 0);
break;
case 2:
QCOMPARE(data.toInt(), (c << 8) + r);
break;
case 3:
QVERIFY(qFuzzyCompare(data.toDouble(), (1.0 + r) / (1.0 + c)));
break;
case 4:
QCOMPARE(data.toULongLong(), ((c << 8) * (r << 8)));
break;
}
}
}
}
}
void tst_AndroidItemModel::resetModel()
{
jModel.callMethod<void>("reset");
QCOMPARE_EQ(qProxy->rowCount(), 0);
QCOMPARE_EQ(qProxy->columnCount(), 0);
}
#include "tst_androiditemmodel.moc"
QTEST_MAIN(tst_AndroidItemModel)