WIP
This commit is contained in:
parent
4f416eb6e2
commit
ffbff8ca96
|
@ -24,7 +24,7 @@ namespace Data {
|
|||
*/
|
||||
namespace TestData {
|
||||
static bool initialized = false;
|
||||
static std::string config, status, folderStats, deviceStats, errors, folderStatus, folderStatus2, folderStatus3, pullErrors, connections, version, empty;
|
||||
static std::string config, status, folderStats, deviceStats, errors, folderStatus, folderStatus2, folderStatus3, pullErrors, connections, version, empty, browse;
|
||||
static std::string events[7];
|
||||
} // namespace TestData
|
||||
|
||||
|
@ -62,10 +62,10 @@ void setupTestData()
|
|||
|
||||
// read mock files for REST-API
|
||||
const char *const fileNames[] = { "config", "status", "folderstats", "devicestats", "errors", "folderstatus-01", "folderstatus-02",
|
||||
"folderstatus-03", "pullerrors-01", "connections", "version", "empty" };
|
||||
"folderstatus-03", "pullerrors-01", "connections", "version", "empty", "browse" };
|
||||
const char *const *fileName = fileNames;
|
||||
for (auto *const testDataVariable : { &config, &status, &folderStats, &deviceStats, &errors, &folderStatus, &folderStatus2, &folderStatus3,
|
||||
&pullErrors, &connections, &version, &empty }) {
|
||||
&pullErrors, &connections, &version, &empty, &browse }) {
|
||||
*testDataVariable = readMockFile(testApp.testFilePath(argsToString("mocks/", *fileName, ".json")));
|
||||
++fileName;
|
||||
}
|
||||
|
@ -171,6 +171,11 @@ MockedReply *MockedReply::forRequest(const QString &method, const QString &path,
|
|||
} else if (folder == QLatin1String("forever-alone")) {
|
||||
buffer = &folderStatus3;
|
||||
}
|
||||
} else if (path == QLatin1String("db/browse") && !query.hasQueryItem(QStringLiteral("prefix"))) {
|
||||
const auto folder = query.queryItemValue(QStringLiteral("folder"));
|
||||
if (folder == QLatin1String("GXWxf-3zgnU")) {
|
||||
buffer = &browse;
|
||||
}
|
||||
} else if (path == QLatin1String("folder/pullerrors")) {
|
||||
const QString folder(query.queryItemValue(QStringLiteral("folder")));
|
||||
if (folder == QLatin1String("GXWxf-3zgnU") && s_eventIndex >= 6) {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
[
|
||||
{
|
||||
"modTime" : "2020-10-02T23:48:52.076996974+02:00",
|
||||
"name" : "100ANDRO",
|
||||
"size" : 128,
|
||||
"type" : "FILE_INFO_TYPE_DIRECTORY"
|
||||
},
|
||||
{
|
||||
"modTime" : "2020-10-09T13:04:42.4410738+02:00",
|
||||
"name" : "Camera",
|
||||
"size" : 128,
|
||||
"type" : "FILE_INFO_TYPE_DIRECTORY",
|
||||
"children" : [
|
||||
{
|
||||
"modTime" : "2020-12-16T23:31:34.5009668+01:00",
|
||||
"name" : "IMG_20201114_124821.jpg",
|
||||
"size" : 10682189,
|
||||
"type" : "FILE_INFO_TYPE_FILE"
|
||||
},
|
||||
{
|
||||
"modTime" : "2020-12-16T23:31:35.0106367+01:00",
|
||||
"name" : "IMG_20201213_122451.jpg",
|
||||
"size" : 7936351,
|
||||
"type" : "FILE_INFO_TYPE_FILE"
|
||||
},
|
||||
{
|
||||
"modTime" : "2020-12-13T12:25:05.017097469+01:00",
|
||||
"name" : "IMG_20201213_122504.jpg",
|
||||
"size" : 8406507,
|
||||
"type" : "FILE_INFO_TYPE_FILE"
|
||||
},
|
||||
{
|
||||
"modTime" : "2020-12-13T12:25:06.127097469+01:00",
|
||||
"name" : "IMG_20201213_122505.jpg",
|
||||
"size" : 8381931,
|
||||
"type" : "FILE_INFO_TYPE_FILE"
|
||||
},
|
||||
{
|
||||
"modTime" : "2020-12-13T12:53:29.707298401+01:00",
|
||||
"name" : "IMG_20201213_125329.jpg",
|
||||
"size" : 4388331,
|
||||
"type" : "FILE_INFO_TYPE_FILE"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -39,7 +39,8 @@ set(TS_FILES translations/${META_PROJECT_NAME}_zh_CN.ts translations/${META_PROJ
|
|||
translations/${META_PROJECT_NAME}_de_DE.ts translations/${META_PROJECT_NAME}_en_US.ts)
|
||||
|
||||
set(QT_TESTS models)
|
||||
set(QT_TEST_SRC_FILES_models syncthingicons.cpp syncthingmodel.cpp syncthingdirectorymodel.cpp syncthingdevicemodel.cpp)
|
||||
set(QT_TEST_SRC_FILES_models syncthingicons.cpp syncthingmodel.cpp syncthingdirectorymodel.cpp syncthingdevicemodel.cpp
|
||||
syncthingfilemodel.cpp)
|
||||
|
||||
# find c++utilities
|
||||
find_package(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.0.0 REQUIRED)
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace CppUtilities;
|
||||
|
||||
|
@ -25,8 +23,11 @@ SyncthingFileModel::SyncthingFileModel(SyncthingConnection &connection, const Sy
|
|||
m_root->modificationTime = dir.lastFileTime;
|
||||
m_root->size = dir.globalStats.bytes;
|
||||
m_root->type = SyncthingItemType::Directory;
|
||||
m_fetchQueue.append(QString());
|
||||
m_connection.browse(m_dirId, QString(), 1, [this](std::vector<std::unique_ptr<SyncthingItem>> &&items, QString &&errorMessage) {
|
||||
Q_UNUSED(errorMessage)
|
||||
|
||||
m_fetchQueue.removeAll(QString());
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -99,7 +100,6 @@ QModelIndex SyncthingFileModel::index(const QString &path) const
|
|||
return res;
|
||||
}
|
||||
}
|
||||
std::cerr << "index for path " << path.toStdString() << ": " << this->path(res).toStdString() << '\n';
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ QModelIndex SyncthingFileModel::parent(const QModelIndex &child) const
|
|||
if (!childItem) {
|
||||
return QModelIndex();
|
||||
}
|
||||
return !childItem->parent ? QModelIndex() : createIndex(static_cast<int>(childItem->index), 0, childItem->parent);
|
||||
return !childItem->parent ? QModelIndex() : createIndex(static_cast<int>(childItem->parent->index), 0, childItem->parent);
|
||||
}
|
||||
|
||||
QVariant SyncthingFileModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
|
@ -263,14 +263,6 @@ static void addLevel(std::vector<std::unique_ptr<SyncthingItem>> &items, int lev
|
|||
addLevel(item->children, level);
|
||||
}
|
||||
}
|
||||
|
||||
static void considerFetched(std::vector<std::unique_ptr<SyncthingItem>> &items)
|
||||
{
|
||||
for (auto &item : items) {
|
||||
item->childrenPopulated = true;
|
||||
considerFetched(item->children);
|
||||
}
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
void SyncthingFileModel::fetchMore(const QModelIndex &parent)
|
||||
|
@ -314,60 +306,30 @@ void SyncthingFileModel::processFetchQueue()
|
|||
m_dirId, path, 1, [this, p = path](std::vector<std::unique_ptr<SyncthingItem>> &&items, QString &&errorMessage) mutable {
|
||||
Q_UNUSED(errorMessage)
|
||||
|
||||
{
|
||||
const auto refreshedIndex = index(p);
|
||||
if (!refreshedIndex.isValid()) {
|
||||
m_fetchQueue.removeAll(p);
|
||||
processFetchQueue();
|
||||
return;
|
||||
}
|
||||
auto *const refreshedItem = reinterpret_cast<SyncthingItem *>(refreshedIndex.internalPointer());
|
||||
if (!refreshedItem->children.empty()) {
|
||||
if (false && refreshedItem == m_root.get()) {
|
||||
beginResetModel();
|
||||
} else {
|
||||
considerFetched(refreshedItem->children);
|
||||
std::cout << "begin remove rows at: " << this->path(refreshedIndex).toStdString() << std::endl;
|
||||
std::cout << " - from 0 to " << static_cast<int>(refreshedItem->children.size() - 1) << std::endl;
|
||||
for (int row = 0; row < static_cast<int>(refreshedItem->children.size()); ++row) {
|
||||
std::cout << " - " << row << " - " << index(row, 0, refreshedIndex).data().toString().toStdString() << std::endl;
|
||||
}
|
||||
beginRemoveRows(refreshedIndex, 0, static_cast<int>(refreshedItem->children.size() - 1));
|
||||
}
|
||||
std::cout << "old row count: " << rowCount(refreshedIndex) << std::endl;
|
||||
refreshedItem->children.clear();
|
||||
if (false && refreshedItem == m_root.get()) {
|
||||
endResetModel();
|
||||
} else {
|
||||
endRemoveRows();
|
||||
}
|
||||
std::cout << "new row count: " << rowCount(refreshedIndex) << std::endl;
|
||||
}
|
||||
m_fetchQueue.removeAll(p);
|
||||
const auto refreshedIndex = index(p);
|
||||
if (!refreshedIndex.isValid()) {
|
||||
processFetchQueue();
|
||||
return;
|
||||
}
|
||||
auto *const refreshedItem = reinterpret_cast<SyncthingItem *>(refreshedIndex.internalPointer());
|
||||
if (!refreshedItem->children.empty()) {
|
||||
beginRemoveRows(refreshedIndex, 0, static_cast<int>(refreshedItem->children.size() - 1));
|
||||
refreshedItem->children.clear();
|
||||
endRemoveRows();
|
||||
}
|
||||
if (!items.empty()) {
|
||||
QTimer::singleShot(400, this, [this, p = std::move(p), items = std::move(items)]() mutable {
|
||||
const auto refreshedIndex = index(p);
|
||||
if (!refreshedIndex.isValid()) {
|
||||
m_fetchQueue.removeAll(p);
|
||||
processFetchQueue();
|
||||
return;
|
||||
}
|
||||
auto *const refreshedItem = reinterpret_cast<SyncthingItem *>(refreshedIndex.internalPointer());
|
||||
const auto last = items.size() - 1;
|
||||
addLevel(items, refreshedItem->level);
|
||||
for (auto &item : items) {
|
||||
item->parent = refreshedItem;
|
||||
}
|
||||
beginInsertRows(
|
||||
refreshedIndex, 0, last < std::numeric_limits<int>::max() ? static_cast<int>(last) : std::numeric_limits<int>::max());
|
||||
refreshedItem->children = std::move(items);
|
||||
refreshedItem->childrenPopulated = true;
|
||||
endInsertRows();
|
||||
|
||||
m_fetchQueue.removeAll(p);
|
||||
processFetchQueue();
|
||||
});
|
||||
const auto last = items.size() - 1;
|
||||
addLevel(items, refreshedItem->level);
|
||||
for (auto &item : items) {
|
||||
item->parent = refreshedItem;
|
||||
}
|
||||
beginInsertRows(refreshedIndex, 0, last < std::numeric_limits<int>::max() ? static_cast<int>(last) : std::numeric_limits<int>::max());
|
||||
refreshedItem->children = std::move(items);
|
||||
refreshedItem->childrenPopulated = true;
|
||||
endInsertRows();
|
||||
}
|
||||
processFetchQueue();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
#include "../syncthingdirectorymodel.h"
|
||||
#include "../syncthingdevicemodel.h"
|
||||
#include "../syncthingdirectorymodel.h"
|
||||
#include "../syncthingfilemodel.h"
|
||||
|
||||
#include <syncthingconnector/syncthingconnection.h>
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
#include <QLocale>
|
||||
#include <QTimer>
|
||||
|
||||
#include <qtutilities/misc/compat.h>
|
||||
|
||||
|
@ -20,6 +21,7 @@ private Q_SLOTS:
|
|||
|
||||
void testDirectoryModel();
|
||||
void testDevicesModel();
|
||||
void testFileModel();
|
||||
|
||||
private:
|
||||
QTimer m_timeout;
|
||||
|
@ -38,7 +40,7 @@ void ModelTests::initTestCase()
|
|||
m_timeout.start();
|
||||
connect(&m_timeout, &QTimer::timeout, this, [this] {
|
||||
m_loop.quit();
|
||||
QFAIL("Timeout exceeded when loading mocked config/status for test");
|
||||
QFAIL("Timeout exceeded");
|
||||
});
|
||||
|
||||
// request config and status and wait until available
|
||||
|
@ -97,5 +99,91 @@ void ModelTests::testDevicesModel()
|
|||
QCOMPARE(model.index(1, 1, dev2Idx).data(), QStringLiteral("dynamic, tcp://192.168.1.3:22000"));
|
||||
}
|
||||
|
||||
void ModelTests::testFileModel()
|
||||
{
|
||||
auto row = 0;
|
||||
const auto *dirInfo = m_connection.findDirInfo(QStringLiteral("GXWxf-3zgnU"), row);
|
||||
QVERIFY(dirInfo);
|
||||
|
||||
// test behavior of empty/unpopulated model
|
||||
auto model = Data::SyncthingFileModel(m_connection, *dirInfo);
|
||||
QCOMPARE(model.rowCount(QModelIndex()), 1);
|
||||
const auto rootIdx = QPersistentModelIndex(model.index(0, 0));
|
||||
QVERIFY(rootIdx.isValid());
|
||||
QVERIFY(!model.index(1, 0).isValid());
|
||||
QCOMPARE(model.rowCount(rootIdx), 1);
|
||||
QCOMPARE(model.index(0, 0, rootIdx).data(), QVariant());
|
||||
QCOMPARE(model.index(1, 0, rootIdx).data(), QVariant());
|
||||
QVERIFY(model.canFetchMore(rootIdx));
|
||||
|
||||
// wait until the root has been updated
|
||||
connect(&model, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) {
|
||||
Q_UNUSED(first)
|
||||
Q_UNUSED(last)
|
||||
if (!parent.parent().isValid() && parent.row() == 0 && parent.column() == 0) {
|
||||
m_timeout.stop();
|
||||
m_loop.quit();
|
||||
}
|
||||
});
|
||||
m_timeout.start();
|
||||
m_loop.exec();
|
||||
|
||||
QVERIFY(rootIdx.isValid());
|
||||
QCOMPARE(model.rowCount(rootIdx), 2);
|
||||
|
||||
// test access to nested folders
|
||||
const auto androidIdx = QPersistentModelIndex(model.index(0, 0, rootIdx));
|
||||
const auto cameraIdx = QPersistentModelIndex(model.index(1, 0, rootIdx));
|
||||
const auto nestedIdx = QPersistentModelIndex(model.index(0, 0, cameraIdx));
|
||||
const auto initialAndroidPtr = androidIdx.constInternalPointer();
|
||||
const auto initialCameraPtr = cameraIdx.constInternalPointer();
|
||||
QVERIFY(androidIdx.isValid());
|
||||
QVERIFY(cameraIdx.isValid());
|
||||
QCOMPARE(androidIdx.parent(), rootIdx);
|
||||
QCOMPARE(cameraIdx.parent(), rootIdx);
|
||||
QCOMPARE(nestedIdx.parent(), cameraIdx);
|
||||
QCOMPARE(model.rowCount(androidIdx), 0);
|
||||
QCOMPARE(model.rowCount(cameraIdx), 5);
|
||||
QCOMPARE(androidIdx.data(), QStringLiteral("100ANDRO"));
|
||||
QCOMPARE(cameraIdx.data(), QStringLiteral("Camera"));
|
||||
QCOMPARE(model.index(0, 0, cameraIdx).data(), QStringLiteral("IMG_20201114_124821.jpg"));
|
||||
QCOMPARE(model.index(0, 1, cameraIdx).data(), QStringLiteral("10.19 MiB"));
|
||||
QCOMPARE(model.index(0, 2, cameraIdx).data(), QStringLiteral("2020-12-16 22:31:34.500"));
|
||||
QCOMPARE(model.index(1, 0, cameraIdx).data(), QStringLiteral("IMG_20201213_122451.jpg"));
|
||||
QCOMPARE(model.index(2, 0, cameraIdx).data(), QStringLiteral("IMG_20201213_122504.jpg"));
|
||||
QCOMPARE(model.index(3, 0, cameraIdx).data(), QStringLiteral("IMG_20201213_122505.jpg"));
|
||||
QCOMPARE(model.index(4, 0, cameraIdx).data(), QStringLiteral("IMG_20201213_125329.jpg"));
|
||||
QCOMPARE(model.index(5, 0, cameraIdx).data(), QVariant());
|
||||
QCOMPARE(model.index(5, 1, cameraIdx).data(), QVariant());
|
||||
QCOMPARE(model.index(5, 2, cameraIdx).data(), QVariant());
|
||||
QCOMPARE(model.index(5, 3, cameraIdx).data(), QVariant());
|
||||
|
||||
// test conversion of indexes to/from paths
|
||||
const auto testPath = QStringLiteral("Camera/IMG_20201213_122504.jpg/");
|
||||
const auto testPathIdx = model.index(2, 0, cameraIdx);
|
||||
QCOMPARE(model.path(testPathIdx), testPath);
|
||||
QCOMPARE(model.index(testPath), testPathIdx);
|
||||
|
||||
// re-load the data again and wait for the update
|
||||
model.fetchMore(rootIdx);
|
||||
m_timeout.start();
|
||||
m_loop.exec();
|
||||
|
||||
// verify that only the root index is still valid (all other indexes have been invalidated)
|
||||
QVERIFY(rootIdx.isValid());
|
||||
QCOMPARE(model.rowCount(rootIdx), 2);
|
||||
QVERIFY(androidIdx.constInternalPointer() != initialAndroidPtr);
|
||||
QVERIFY(!androidIdx.isValid());
|
||||
QVERIFY(cameraIdx.constInternalPointer() != initialCameraPtr);
|
||||
QVERIFY(!cameraIdx.isValid());
|
||||
QVERIFY(!nestedIdx.isValid());
|
||||
|
||||
// verify that data was re-loaded
|
||||
const auto androidIdx2 = QPersistentModelIndex(model.index(0, 0, rootIdx));
|
||||
const auto cameraIdx2 = QPersistentModelIndex(model.index(1, 0, rootIdx));
|
||||
QCOMPARE(androidIdx2.data(), QStringLiteral("100ANDRO"));
|
||||
QCOMPARE(cameraIdx2.data(), QStringLiteral("Camera"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(ModelTests)
|
||||
#include "models.moc"
|
||||
|
|
Loading…
Reference in New Issue