Commit 1db5b4a1 authored by chriadam's avatar chriadam

[qtcontacts-sqlite] Store display label group to database. Contributes to JB#44742

parent 2f621de4
......@@ -142,7 +142,8 @@ static QVariant dateValue(const QVariant &columnValue)
static const FieldInfo displayLabelFields[] =
{
{ QContactDisplayLabel::FieldLabel, "displayLabel", LocalizedField }
{ QContactDisplayLabel::FieldLabel, "displayLabel", LocalizedField },
{ QContactDisplayLabel__FieldLabelGroup, "displayLabelGroup", LocalizedField }
};
static const FieldInfo nameFields[] =
......@@ -1935,7 +1936,30 @@ QContactManager::Error ContactReader::queryContacts(
QContactManager::Error err = QContactManager::NoError;
const QString idsQueryStatement(QString::fromLatin1(
"SELECT Contacts.* "
"SELECT " // Contacts.*, but order can change due to schema upgrades, so list manually.
"Contacts.contactId, "
"Contacts.displayLabel, "
"Contacts.displayLabelGroup, "
"Contacts.firstName, "
"Contacts.lowerFirstName, "
"Contacts.lastName, "
"Contacts.lowerLastName, "
"Contacts.middleName, "
"Contacts.prefix, "
"Contacts.suffix, "
"Contacts.customLabel, "
"Contacts.syncTarget, "
"Contacts.created, "
"Contacts.modified, "
"Contacts.gender, "
"Contacts.isFavorite, "
"Contacts.hasPhoneNumber, "
"Contacts.hasEmailAddress, "
"Contacts.hasOnlineAccount, "
"Contacts.isOnline, "
"Contacts.isDeactivated, "
"Contacts.isIncidental, "
"Contacts.type "
"FROM temp.%1 "
"CROSS JOIN Contacts ON temp.%1.contactId = Contacts.contactId " // Cross join ensures we scan the temp table first
"ORDER BY temp.%1.rowId ASC").arg(tableName));
......@@ -2117,22 +2141,22 @@ QContactManager::Error ContactReader::queryContacts(
contact.setId(id);
QString persistedDL = contactQuery.value(1).toString();
if (!persistedDL.isEmpty())
ContactsEngine::setContactDisplayLabel(&contact, persistedDL);
QString displayLabelGroup = contactQuery.value(2).toString();
ContactsEngine::setContactDisplayLabel(&contact, persistedDL, displayLabelGroup);
QContactName name;
setValue(&name, QContactName::FieldFirstName , contactQuery.value(2));
setValue(&name, QContactName::FieldFirstName , contactQuery.value(3));
// ignore lowerFirstName
setValue(&name, QContactName::FieldLastName , contactQuery.value(4));
setValue(&name, QContactName::FieldLastName , contactQuery.value(5));
// ignore lowerLastName
setValue(&name, QContactName::FieldMiddleName , contactQuery.value(6));
setValue(&name, QContactName::FieldPrefix , contactQuery.value(7));
setValue(&name, QContactName::FieldSuffix , contactQuery.value(8));
setValue(&name, QContactName__FieldCustomLabel, contactQuery.value(9));
setValue(&name, QContactName::FieldMiddleName , contactQuery.value(7));
setValue(&name, QContactName::FieldPrefix , contactQuery.value(8));
setValue(&name, QContactName::FieldSuffix , contactQuery.value(9));
setValue(&name, QContactName__FieldCustomLabel, contactQuery.value(10));
if (!name.isEmpty())
contact.saveDetail(&name);
const QString syncTarget(contactQuery.value(10).toString());
const QString syncTarget(contactQuery.value(11).toString());
QContactSyncTarget starget;
setValue(&starget, QContactSyncTarget::FieldSyncTarget, syncTarget);
......@@ -2140,27 +2164,27 @@ QContactManager::Error ContactReader::queryContacts(
contact.saveDetail(&starget);
QContactTimestamp timestamp;
setValue(&timestamp, QContactTimestamp::FieldCreationTimestamp , ContactsDatabase::fromDateTimeString(contactQuery.value(11).toString()));
setValue(&timestamp, QContactTimestamp::FieldModificationTimestamp, ContactsDatabase::fromDateTimeString(contactQuery.value(12).toString()));
setValue(&timestamp, QContactTimestamp::FieldCreationTimestamp , ContactsDatabase::fromDateTimeString(contactQuery.value(12).toString()));
setValue(&timestamp, QContactTimestamp::FieldModificationTimestamp, ContactsDatabase::fromDateTimeString(contactQuery.value(13).toString()));
QContactGender gender;
// Gender is an enum in qtpim
QString genderText = contactQuery.value(13).toString();
QString genderText = contactQuery.value(14).toString();
gender.setGender(static_cast<QContactGender::GenderField>(genderText.toInt()));
contact.saveDetail(&gender);
QContactFavorite favorite;
setValue(&favorite, QContactFavorite::FieldFavorite, contactQuery.value(14).toBool());
setValue(&favorite, QContactFavorite::FieldFavorite, contactQuery.value(15).toBool());
if (!favorite.isEmpty())
contact.saveDetail(&favorite);
QContactStatusFlags flags;
flags.setFlag(QContactStatusFlags::HasPhoneNumber, contactQuery.value(15).toBool());
flags.setFlag(QContactStatusFlags::HasEmailAddress, contactQuery.value(16).toBool());
flags.setFlag(QContactStatusFlags::HasOnlineAccount, contactQuery.value(17).toBool());
flags.setFlag(QContactStatusFlags::IsOnline, contactQuery.value(18).toBool());
flags.setFlag(QContactStatusFlags::IsDeactivated, contactQuery.value(19).toBool());
flags.setFlag(QContactStatusFlags::IsIncidental, contactQuery.value(20).toBool());
flags.setFlag(QContactStatusFlags::HasPhoneNumber, contactQuery.value(16).toBool());
flags.setFlag(QContactStatusFlags::HasEmailAddress, contactQuery.value(17).toBool());
flags.setFlag(QContactStatusFlags::HasOnlineAccount, contactQuery.value(18).toBool());
flags.setFlag(QContactStatusFlags::IsOnline, contactQuery.value(19).toBool());
flags.setFlag(QContactStatusFlags::IsDeactivated, contactQuery.value(20).toBool());
flags.setFlag(QContactStatusFlags::IsIncidental, contactQuery.value(21).toBool());
if (flags.testFlag(QContactStatusFlags::IsDeactivated)) {
QContactDeactivated deactivated;
......@@ -2171,7 +2195,7 @@ QContactManager::Error ContactReader::queryContacts(
contact.saveDetail(&incidental);
}
int contactType = contactQuery.value(21).toInt();
int contactType = contactQuery.value(22).toInt();
QContactType typeDetail = contact.detail<QContactType>();
typeDetail.setType(static_cast<QContactType::TypeValues>(contactType));
contact.saveDetail(&typeDetail);
......
......@@ -63,6 +63,7 @@ static const char *createContactsTable =
"\n CREATE TABLE Contacts ("
"\n contactId INTEGER PRIMARY KEY ASC AUTOINCREMENT,"
"\n displayLabel TEXT,"
"\n displayLabelGroup TEXT," // Don't specify `COLLATE localeCollation` as a change in locale would be equivalent to a database corruption
"\n firstName TEXT,"
"\n lowerFirstName TEXT,"
"\n lastName TEXT,"
......@@ -1286,6 +1287,10 @@ static const char *upgradeVersion16[] = {
"PRAGMA user_version=17",
0 // NULL-terminated
};
static const char *upgradeVersion17[] = {
"PRAGMA user_version=18",
0 // NULL-terminated
};
typedef bool (*UpgradeFunction)(QSqlDatabase &database);
......@@ -1679,6 +1684,81 @@ static bool updateStorageTypes(QSqlDatabase &database)
return true;
}
static bool addDisplayLabelGroup(QSqlDatabase &database)
{
// add the display label group (e.g. ribbon group / name bucket) column
{
QSqlQuery alterQuery(database);
const QString statement = QStringLiteral("ALTER TABLE Contacts ADD COLUMN displayLabelGroup TEXT");
if (!alterQuery.prepare(statement)) {
QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Failed to prepare add display label group column query: %1\n%2")
.arg(alterQuery.lastError().text())
.arg(statement));
return false;
}
if (!alterQuery.exec()) {
QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Failed to add display label group column: %1\n%2")
.arg(alterQuery.lastError().text())
.arg(statement));
return false;
}
alterQuery.finish();
}
// for every single contact in our database, read the data required to generate the display label group data.
QVariantList contactIds;
QVariantList displayLabelGroups;
{
QSqlQuery selectQuery(database);
selectQuery.setForwardOnly(true);
const QString statement = QStringLiteral("SELECT contactId, firstName, lastName, displayLabel FROM Contacts");
if (!selectQuery.prepare(statement)) {
QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Failed to prepare display label groups data selection query: %1\n%2")
.arg(selectQuery.lastError().text())
.arg(statement));
return false;
}
if (!selectQuery.exec()) {
QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Failed to select display label groups data: %1\n%2")
.arg(selectQuery.lastError().text())
.arg(statement));
return false;
}
while (selectQuery.next()) {
const quint32 dbId = selectQuery.value(0).toUInt();
const QString firstName = selectQuery.value(1).toString();
const QString lastName = selectQuery.value(2).toString();
const QString displayLabel = selectQuery.value(3).toString();
contactIds.append(dbId);
displayLabelGroups.append(ContactsDatabase::determineDisplayLabelGroup(firstName, lastName, displayLabel));
}
selectQuery.finish();
}
// now write the generated data back to the database.
{
QSqlQuery updateQuery(database);
const QString statement = QStringLiteral("UPDATE Contacts SET displayLabelGroup = ? WHERE contactId = ?");
if (!updateQuery.prepare(statement)) {
QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Failed to prepare update display label groups query: %1\n%2")
.arg(updateQuery.lastError().text())
.arg(statement));
return false;
}
updateQuery.addBindValue(displayLabelGroups);
updateQuery.addBindValue(contactIds);
if (!updateQuery.execBatch()) {
QTCONTACTS_SQLITE_WARNING(QString::fromLatin1("Failed to update display label groups: %1\n%2")
.arg(updateQuery.lastError().text())
.arg(statement));
return false;
}
updateQuery.finish();
}
return true;
}
struct UpgradeOperation {
UpgradeFunction fn;
const char **statements;
......@@ -1702,9 +1782,10 @@ static UpgradeOperation upgradeVersions[] = {
{ 0, upgradeVersion14 },
{ 0, upgradeVersion15 },
{ updateStorageTypes, upgradeVersion16 },
{ addDisplayLabelGroup, upgradeVersion17 },
};
static const int currentSchemaVersion = 17;
static const int currentSchemaVersion = 18;
static bool execute(QSqlDatabase &database, const QString &statement)
{
......@@ -3017,6 +3098,30 @@ QDateTime ContactsDatabase::fromDateTimeString(const QString &s)
return QDateTime(datepart, timepart, Qt::UTC);
}
QString ContactsDatabase::determineDisplayLabelGroup(const QString &firstName, const QString &lastName, const QString &displayLabel)
{
// XXX TODO!
// This is a hacky example only.
// In reality, we need to load plugins which do this properly for e.g. Chinese etc also.
// And we need to check the system settings to determine if first or last name should be preferred.
const QString preferred =
!lastName.trimmed().isEmpty() ? lastName :
!firstName.trimmed().isEmpty() ? firstName : displayLabel;
const QChar firstChar = preferred.trimmed().at(0).toUpper();
if (firstChar >= QChar('A') && firstChar <= QChar('Z')) {
return QString(firstChar);
}
if (firstChar.isDigit()) {
return QString(QChar('#'));
}
return QString(QChar('!'));
}
#include "../extensions/qcontactdeactivated_impl.h"
#include "../extensions/qcontactincidental_impl.h"
#include "../extensions/qcontactoriginmetadata_impl.h"
......
......@@ -162,6 +162,8 @@ public:
// Output is UTC
static QDateTime fromDateTimeString(const QString &s);
static QString determineDisplayLabelGroup(const QString &firstName, const QString &lastName, const QString &displayLabel);
private:
QSqlDatabase m_database;
ContactsTransientStore m_transientStore;
......
......@@ -1176,13 +1176,16 @@ QList<QContactType::TypeValues> ContactsEngine::supportedContactTypes() const
void ContactsEngine::regenerateDisplayLabel(QContact &contact) const
{
QContactManager::Error displayLabelError = QContactManager::NoError;
QString label = synthesizedDisplayLabel(contact, &displayLabelError);
const QString label = synthesizedDisplayLabel(contact, &displayLabelError);
if (displayLabelError != QContactManager::NoError) {
QTCONTACTS_SQLITE_DEBUG(QString::fromLatin1("Unable to regenerate displayLabel for contact: %1").arg(ContactId::toString(contact)));
return;
}
setContactDisplayLabel(&contact, label);
const QString group = ContactsDatabase::determineDisplayLabelGroup(contact.detail<QContactName>().firstName(),
contact.detail<QContactName>().lastName(),
label);
setContactDisplayLabel(&contact, label, group);
}
bool ContactsEngine::fetchSyncContacts(const QString &syncTarget, const QDateTime &lastSync, const QList<QContactId> &exportedIds,
......@@ -1282,11 +1285,24 @@ bool ContactsEngine::removeOOB(const QString &scope)
return writer()->removeOOB(scope, QStringList());
}
bool ContactsEngine::setContactDisplayLabel(QContact *contact, const QString &label)
bool ContactsEngine::setContactDisplayLabel(QContact *contact, const QString &label, const QString &group)
{
QContactDisplayLabel detail(contact->detail<QContactDisplayLabel>());
detail.setLabel(label);
return contact->saveDetail(&detail);
bool needSave = false;
if (!label.trimmed().isEmpty()) {
detail.setLabel(label);
needSave = true;
}
if (!group.trimmed().isEmpty()) {
detail.setValue(QContactDisplayLabel__FieldLabelGroup, group);
needSave = true;
}
if (needSave) {
return contact->saveDetail(&detail);
}
return true;
}
QString ContactsEngine::normalizedPhoneNumber(const QString &input)
......
......@@ -159,7 +159,7 @@ public:
bool removeOOB(const QString &scope, const QStringList &keys);
bool removeOOB(const QString &scope);
static bool setContactDisplayLabel(QContact *contact, const QString &label);
static bool setContactDisplayLabel(QContact *contact, const QString &label, const QString &group);
static QString normalizedPhoneNumber(const QString &input);
......
......@@ -5541,6 +5541,7 @@ ContactsDatabase::Query ContactWriter::bindContactDetails(const QContact &contac
const QString insertContact(QStringLiteral(
" INSERT INTO Contacts ("
" displayLabel,"
" displayLabelGroup,"
" firstName,"
" lowerFirstName,"
" lastName,"
......@@ -5562,6 +5563,7 @@ ContactsDatabase::Query ContactWriter::bindContactDetails(const QContact &contac
" isIncidental)"
" VALUES ("
" :displayLabel,"
" :displayLabelGroup,"
" :firstName,"
" :lowerFirstName,"
" :lastName,"
......@@ -5585,6 +5587,7 @@ ContactsDatabase::Query ContactWriter::bindContactDetails(const QContact &contac
const QString updateContact(QStringLiteral(
" UPDATE Contacts SET"
" displayLabel = :displayLabel,"
" displayLabelGroup = :displayLabelGroup,"
" firstName = :firstName,"
" lowerFirstName = :lowerFirstName,"
" lastName = :lastName,"
......@@ -5610,34 +5613,36 @@ ContactsDatabase::Query ContactWriter::bindContactDetails(const QContact &contac
ContactsDatabase::Query query(m_database.prepare(update ? updateContact : insertContact));
QContactDisplayLabel label = contact.detail<QContactDisplayLabel>();
query.bindValue(0, label.label().trimmed());
const QContactName name = contact.detail<QContactName>();
const QString firstName(name.value<QString>(QContactName::FieldFirstName).trimmed());
const QString lastName(name.value<QString>(QContactName::FieldLastName).trimmed());
query.bindValue(1, firstName);
query.bindValue(2, firstName.toLower());
query.bindValue(3, lastName);
query.bindValue(4, lastName.toLower());
query.bindValue(5, name.value<QString>(QContactName::FieldMiddleName).trimmed());
query.bindValue(6, name.value<QString>(QContactName::FieldPrefix).trimmed());
query.bindValue(7, name.value<QString>(QContactName::FieldSuffix).trimmed());
query.bindValue(8, name.value<QString>(QContactName__FieldCustomLabel).trimmed());
QContactDisplayLabel label = contact.detail<QContactDisplayLabel>();
const QString displayLabel = label.label().trimmed();
query.bindValue(0, displayLabel);
query.bindValue(1, ContactsDatabase::determineDisplayLabelGroup(firstName, lastName, displayLabel));
query.bindValue(2, firstName);
query.bindValue(3, firstName.toLower());
query.bindValue(4, lastName);
query.bindValue(5, lastName.toLower());
query.bindValue(6, name.value<QString>(QContactName::FieldMiddleName).trimmed());
query.bindValue(7, name.value<QString>(QContactName::FieldPrefix).trimmed());
query.bindValue(8, name.value<QString>(QContactName::FieldSuffix).trimmed());
query.bindValue(9, name.value<QString>(QContactName__FieldCustomLabel).trimmed());
const QString syncTarget(contact.detail<QContactSyncTarget>().syncTarget());
query.bindValue(9, syncTarget);
query.bindValue(10, syncTarget);
const QContactTimestamp timestamp = contact.detail<QContactTimestamp>();
query.bindValue(10, ContactsDatabase::dateTimeString(timestamp.value<QDateTime>(QContactTimestamp::FieldCreationTimestamp).toUTC()));
query.bindValue(11, ContactsDatabase::dateTimeString(timestamp.value<QDateTime>(QContactTimestamp::FieldModificationTimestamp).toUTC()));
query.bindValue(11, ContactsDatabase::dateTimeString(timestamp.value<QDateTime>(QContactTimestamp::FieldCreationTimestamp).toUTC()));
query.bindValue(12, ContactsDatabase::dateTimeString(timestamp.value<QDateTime>(QContactTimestamp::FieldModificationTimestamp).toUTC()));
const QContactGender gender = contact.detail<QContactGender>();
query.bindValue(12, QString::number(static_cast<int>(gender.gender())));
query.bindValue(13, QString::number(static_cast<int>(gender.gender())));
const QContactFavorite favorite = contact.detail<QContactFavorite>();
query.bindValue(13, favorite.isFavorite());
query.bindValue(14, favorite.isFavorite());
// Does this contact contain the information needed to update hasPhoneNumber?
bool hasPhoneNumberKnown = definitionMask.isEmpty() || detailListContains<QContactPhoneNumber>(definitionMask);
......@@ -5672,26 +5677,26 @@ ContactsDatabase::Query ContactWriter::bindContactDetails(const QContact &contac
}
if (update) {
query.bindValue(14, hasPhoneNumberKnown);
query.bindValue(15, hasPhoneNumber);
query.bindValue(16, hasEmailAddressKnown);
query.bindValue(17, hasEmailAddress);
query.bindValue(18, hasOnlineAccountKnown);
query.bindValue(19, hasOnlineAccount);
query.bindValue(20, isOnlineKnown);
query.bindValue(21, isOnline);
query.bindValue(22, isDeactivatedKnown);
query.bindValue(23, isDeactivated);
query.bindValue(24, contactId);
query.bindValue(15, hasPhoneNumberKnown);
query.bindValue(16, hasPhoneNumber);
query.bindValue(17, hasEmailAddressKnown);
query.bindValue(18, hasEmailAddress);
query.bindValue(19, hasOnlineAccountKnown);
query.bindValue(20, hasOnlineAccount);
query.bindValue(21, isOnlineKnown);
query.bindValue(22, isOnline);
query.bindValue(23, isDeactivatedKnown);
query.bindValue(24, isDeactivated);
query.bindValue(25, contactId);
} else {
query.bindValue(14, hasPhoneNumber);
query.bindValue(15, hasEmailAddress);
query.bindValue(16, hasOnlineAccount);
query.bindValue(17, isOnline);
query.bindValue(18, isDeactivated);
query.bindValue(15, hasPhoneNumber);
query.bindValue(16, hasEmailAddress);
query.bindValue(17, hasOnlineAccount);
query.bindValue(18, isOnline);
query.bindValue(19, isDeactivated);
// Incidental state only applies to creation
query.bindValue(19, !contact.details<QContactIncidental>().isEmpty());
query.bindValue(20, !contact.details<QContactIncidental>().isEmpty());
}
return query;
......
......@@ -39,6 +39,7 @@
#include <QContactPhoneNumber>
#include <QContactAvatar>
#include <QContactName>
#include <QContactDisplayLabel>
// Defines the extended values supported by qtcontacts-sqlite
......@@ -52,6 +53,9 @@ static const int QContactDetail__FieldNonexportable = (QContactDetail::FieldLink
// In QContactName, we support the customLabel property
static const int QContactName__FieldCustomLabel = (QContactName::FieldSuffix+1);
// In QContactDisplayLabel, we support the labelGroup property
static const int QContactDisplayLabel__FieldLabelGroup = (QContactDisplayLabel::FieldLabel+1);
// In QContactOnlineAccount we support the following properties:
// AccountPath - identifying path value for the account
// AccountIconPath - path to an icon indicating the service type of the account
......
......@@ -1923,6 +1923,11 @@ void tst_QContactManagerFiltering::sorting_data()
// Note - the current display label algorithm follows that of nemo-qml-plugin-contacts, and does not include prefix
//newMRow("display label insensitive, binary collation", manager) << manager << dldef << dlfld << asc << false << 0 << ci << "bcdefgaijhk" << "efg"; // the display label is synthesized so that A has "Sir" at the start of it (instead of "Aaron").
//newMRow("display label sensitive, binary collation", manager) << manager << dldef << dlfld << asc << false << 0 << cs << "bcdefgaikjh" << "efg";
#ifdef DISPLAY_LABEL_GROUP_STORAGE_SUPPORTED
FieldIdentifier dlgfld = QContactDisplayLabel__FieldLabelGroup;
newMRow("display label group descending, locale collation", manager) << manager << dldef << dlgfld << asc << false << 0 << cs << "abcdefghijk" << "hijk";
#endif
}
}
......@@ -1934,7 +1939,7 @@ void tst_QContactManagerFiltering::sorting()
QFETCH(int, directioni);
QFETCH(bool, setbp);
QFETCH(int, blankpolicyi);
QFETCH(int, casesensitivityi);
QFETCH(int, casesensitivityi);
QFETCH(QString, expected);
QFETCH(QString, unstable);
......
......@@ -52,6 +52,8 @@
#include <QDateTime>
#include <QtDebug>
#include "../../../src/extensions/qtcontacts-extensions.h"
QTCONTACTS_USE_NAMESPACE
#include <QContactIdFilter>
......@@ -1161,7 +1163,7 @@ qint64 simpleFilterAndSort(QContactManager &manager, bool quickMode)
qDebug() << "Starting save (chunks) / fetch (filter + sort) / delete (all) test...";
QList<QContact> testData, testData2;
const int chunkSize = quickMode ? 25 : 50;
const int prefillCount = quickMode ? 250 : 500;
const int prefillCount = quickMode ? 250 : 1000;
for (int i = 0; i < prefillCount/2; ++i) {
testData.append(generateContact(QString::fromLatin1("simpleFilterAndSort"), true));
testData2.append(generateContact(QString::fromLatin1("simpleFilterAndSort2"), true));
......@@ -1182,7 +1184,7 @@ qint64 simpleFilterAndSort(QContactManager &manager, bool quickMode)
listDisplayFetchHint.setDetailTypesHint(QList<QContactDetail::DetailType>()
<< QContactDisplayLabel::Type << QContactName::Type << QContactAvatar::Type);
QContactSortOrder sort;
sort.setDetailType(QContactName::Type, QContactName::FieldLastName);
sort.setDetailType(QContactDisplayLabel::Type, QContactDisplayLabel__FieldLabelGroup);
QContactDetailFilter phoneFilter;
phoneFilter.setDetailType(QContactPhoneNumber::Type); // existence filter, don't care about value.
QContactDetailFilter syncTargetFilter;
......
......@@ -71,6 +71,9 @@
// qtpim doesn't support the customLabel field natively, but qtcontact-sqlite provides it
#define CUSTOM_LABEL_STORAGE_SUPPORTED
// qtpim doesn't support the displayLabelGroup field natively, but qtcontacts-sqlite provides it
#define DISPLAY_LABEL_GROUP_STORAGE_SUPPORTED
// Eventually these will make it into qtestcase.h
// but we might need to tweak the timeout values here.
#ifndef QTRY_COMPARE
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment