qtmobility/plugins/contacts/symbiansim/tsrc/tst_simcm/tst_simcm.cpp
changeset 14 6fbed849b4f4
parent 11 06b8e2af4411
--- a/qtmobility/plugins/contacts/symbiansim/tsrc/tst_simcm/tst_simcm.cpp	Fri Jun 11 14:26:25 2010 +0300
+++ b/qtmobility/plugins/contacts/symbiansim/tsrc/tst_simcm/tst_simcm.cpp	Wed Jun 23 19:08:38 2010 +0300
@@ -53,6 +53,9 @@
 
 QTM_USE_NAMESPACE
 
+typedef QList<QContactLocalId> QContactIds;
+Q_DECLARE_METATYPE(QContactIds);
+
 #ifndef QTRY_COMPARE
 #define QTRY_COMPARE(__expr, __expected) \
     do { \
@@ -82,8 +85,6 @@
     virtual ~tst_SimCM();
 
 public slots:
-    void init();
-    void cleanup();
     void initTestCase();
     void cleanupTestCase();
 
@@ -105,18 +106,28 @@
     void updateContactDetail();
     void batchOperations_data();
     void batchOperations();
+    void detailFilter_data();
+    void detailFilter();
+    void sortingAdn_data();
+    void sortingAdn();
 
     /* Test cases that take no data */
     void signalEmission();
     void sdnContacts();
+    void fillSlots();
 
 private:
     void initManager(QString simStore);
     void getEtelStoreInfoL(const TDesC &phonebook, TDes8 &infoPckg) const;
     bool isContactSupported(QContact contact);
     void parseDetails(QContact &contact, QStringList details, QList<QContactDetail> &parsedDetails);
-    void compareDetails(QContact contact, QList<QContactDetail> expectedDetails);
+    bool compareDetails(QContact contact, QList<QContactDetail> expectedDetails);
     QContact createContact(QString name, QString number);
+    QContact saveContact(QString name, QString number);
+    void dumpStoreInfo();
+    bool compareContactLists(QList<QContact> lista, QList<QContact> listb);
+    bool compareContacts(QContact ca, QContact cb);
+    
 
 private:
     QContactManager* m_cm;
@@ -139,20 +150,6 @@
 {
 }
 
-void tst_SimCM::init()
-{
-    // remove all contacts
-    QList<QContactLocalId> ids = m_cm->contactIds();
-    m_cm->removeContacts(ids, 0);   
-}
-
-void tst_SimCM::cleanup()
-{
-    // remove all contacts
-    QList<QContactLocalId> ids = m_cm->contactIds();
-    m_cm->removeContacts(ids, 0);   
-}
-
 void tst_SimCM::initTestCase()
 {
     initManager(QString());
@@ -160,10 +157,15 @@
     // TODO: how about other stores?
     TRAPD(err, getEtelStoreInfoL(KETelIccAdnPhoneBook, m_etelStoreInfoPckg));
     QCOMPARE(err, KErrNone);
+
+    // remove all contacts
+    m_cm->removeContacts(m_cm->contactIds(), 0);   
 }
 
 void tst_SimCM::cleanupTestCase()
 {
+    // remove all contacts
+    m_cm->removeContacts(m_cm->contactIds(), 0);   
     delete m_cm;
     m_cm = 0;
 }
@@ -354,11 +356,13 @@
     QTest::addColumn<int>("expectedResult"); // 1 = pass, 0 = fail, -1 = depends on the SIM card
     QTest::addColumn<QString>("expectedDisplayLabel");
     QTest::addColumn<QStringList>("details"); // format is <detail definition name>:<field name>:<value>
-    QString unnamedLabel("Unnamed");
     QString es;
     QString tooLongText("James Hunt the 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890th");
 
-    // TODO: what name field to use for a sim contact name?
+#ifdef __WINS__
+    qWarning("Etel test server (emulator) does report an error when saving if contact has too long details!");
+#endif
+
     // Note: With the current implementation the value must not contain a ':' character
     QTest::newRow("ADN custom label")
         << QString("ADN")
@@ -391,7 +395,11 @@
 
     QTest::newRow("ADN too long custom label")
         << QString("ADN")
-        << 1 // expected to pass. Note: too long display label is truncated
+#ifdef __WINS__
+        << 1 // Etel test server does not enforce the max lengths
+#else
+        << 0 // expected to fail
+#endif
         << tooLongText
         << (QStringList()
             << (QString("Name:CustomLabel:").append(tooLongText)));
@@ -406,7 +414,11 @@
 
     QTest::newRow("ADN custom label and too long nick name")
         << QString("ADN")
-        << -1 // Depends on SIM card support (some cards support second name)
+#ifdef __WINS__
+        << 1 // Etel test server does not enforce the max lengths
+#else
+        << 0 // expected to fail
+#endif
         << "James Hunt"
         << (QStringList()
             << "Name:CustomLabel:James Hunt"
@@ -415,10 +427,18 @@
     QTest::newRow("ADN phone number")
         << QString("ADN")
         << 1
-        << unnamedLabel
+        << es
         << (QStringList()
             << "PhoneNumber:PhoneNumber:+44752222222");
 
+    QTest::newRow("ADN empty name and phone number")
+        << QString("ADN")
+        << 1
+        << es
+        << (QStringList()
+            << "Name:CustomLabel:"
+            << "PhoneNumber:PhoneNumber:+44752222222");
+
     QTest::newRow("ADN custom label and phone number")
         << QString("ADN")
         << 1
@@ -514,7 +534,11 @@
 
     QTest::newRow("ADN custom label and too long email")
         << QString("ADN")
+#ifdef __WINS__
+        << 1 // Etel test server does not enforce the max lengths
+#else
         << 0 // long enough e-mail to fail on any SIM card
+#endif
         << "James Hunt"
         << (QStringList()
             << "Name:CustomLabel:James Hunt"
@@ -620,14 +644,19 @@
         QVERIFY(contact.id() != QContactId());
 
         // verify that the details were saved as expected
-        compareDetails(contact, expectedDetails);
+        QVERIFY(compareDetails(contact, expectedDetails));
 
-        // verify display label, allow truncating to the max text length
-        QCOMPARE(contact.displayLabel(), expectedDisplayLabel.left(m_etelStoreInfo.iMaxTextLength));
+        // verify display label
+        QCOMPARE(contact.displayLabel(), expectedDisplayLabel);
 
         // TODO: verify that no extra details were added?
         //?QCOMPARE(contact.details().count(), detailsUnderTest.count() + 2);
 
+        // Read the contact from SIM and re-verify the result
+        contact = m_cm->contact(contact.localId());
+        QVERIFY(compareDetails(contact, expectedDetails));
+        QCOMPARE(contact.displayLabel(), expectedDisplayLabel);
+
         // verify contact removal
         QVERIFY(m_cm->removeContact(contact.localId()));
         QCOMPARE(m_cm->error(), QContactManager::NoError);
@@ -659,7 +688,8 @@
         << QString("SDN")
         << int(0); // You cannot save a contact to SDN
 
-    // TODO: How to save to FDN? A dialog for PIN2 should be shown...
+    // Requires that PIN2 has been given (for example by activating and
+    // de-activating FDN via S60 Phonebook)
     QTest::newRow("FDN")
         << QString("FDN")
         << int(1);
@@ -848,7 +878,7 @@
     }
     QVERIFY(m_cm->saveContact(&contact));
     QCOMPARE(m_cm->error(), QContactManager::NoError);
-    compareDetails(contact, parsedDetails);
+    QVERIFY(compareDetails(contact, parsedDetails));
 
     // 3. Update contact detail and verify result
     foreach (const QContactDetail& detail, parsedDetails) {
@@ -862,7 +892,7 @@
     }
     QVERIFY(m_cm->saveContact(&contact));
     QCOMPARE(m_cm->error(), QContactManager::NoError);
-    compareDetails(contact, parsedDetails);
+    QVERIFY(compareDetails(contact, parsedDetails));
 
     // 4. Remove the contact
     QVERIFY(m_cm->removeContact(contact.localId()));
@@ -884,7 +914,8 @@
         << 10
         << false; // You cannot save contacts to SDN
 
-    // TODO: How to save to FDN? A dialog for PIN2 should be shown...
+    // Requires that PIN2 has been given (for example by activating and
+    // de-activating FDN via S60 Phonebook)
     QTest::newRow("FDN")
         << QString("FDN")
         << 10
@@ -961,6 +992,122 @@
     }
 }
 
+void tst_SimCM::detailFilter_data()
+{
+    QTest::addColumn<QString>("detailName");
+    QTest::addColumn<QString>("detailField");
+    QTest::addColumn<QString>("value");
+    QTest::addColumn<int>("flags");
+    QTest::addColumn<bool>("filterSupported");
+    QTest::addColumn<QString>("expected");
+
+    // Phone number
+    
+    QString detail = QContactPhoneNumber::DefinitionName;
+    QString field = QContactPhoneNumber::FieldNumber;
+    
+    QTest::newRow("phonenumber=123456789, flags=MatchExactly")
+        << detail << field << "123456789" << (int) QContactFilter::MatchExactly << false << "a";
+    
+    QTest::newRow("phonenumber=123456789, flags=MatchContains")
+        << detail << field << "123456789" << (int) QContactFilter::MatchContains << false << "abc";
+    
+    QTest::newRow("phonenumber=#, flags=MatchContains")
+        << detail << field << "#" << (int) QContactFilter::MatchContains << false << "f";
+    
+    QTest::newRow("phonenumber=p, flags=MatchContains")
+        << detail << field << "p" << (int) QContactFilter::MatchContains << false << "e";
+        
+    QTest::newRow("phonenumber=0, flags=MatchStartsWith")
+        << detail << field << "0" << (int) QContactFilter::MatchStartsWith << false << "defi";    
+    
+    QTest::newRow("phonenumber=012, flags=MatchEndsWith")
+        << detail << field << "012" << (int) QContactFilter::MatchEndsWith << false << "c";
+    
+    QTest::newRow("phonenumber=+358505555555, flags=MatchPhoneNumber")
+        << detail << field << "+358505555555" << (int) QContactFilter::MatchPhoneNumber << true << "ij";
+    
+    QTest::newRow("phonenumber=313, flags=MatchPhoneNumber")
+        << detail << field << "313" << (int) QContactFilter::MatchPhoneNumber << true << "h";
+    
+    // Custom label
+    detail = (QLatin1String) QContactName::DefinitionName;
+    field = (QLatin1String) QContactName::FieldCustomLabel;
+    
+    QTest::newRow("customlabel=frederik")
+            << detail << field << "frederik" << 0 << false << "c";
+    
+    QTest::newRow("customlabel=Kallasvuo flags=MatchContains")
+            << detail << field << "Kallasvuo" << (int) (QContactFilter::MatchContains) << false << "d";
+    
+    QTest::newRow("customlabel=Matti flags=MatchStartsWith")
+            << detail << field << "Matti" << (int) (QContactFilter::MatchStartsWith) << false << "b";
+    
+    QTest::newRow("customlabel=co flags=MatchEndsWith")
+            << detail << field << "co" << (int) (QContactFilter::MatchEndsWith) << false << "f";
+    
+    // ITU-T standard keypad collation:
+    // 2 = abc, 3 = def, 4 = ghi, 5 = jkl, 6 = mno, 7 = pqrs, 8 = tuv, 9 = wxyz, 0 = space
+    
+    QTest::newRow("customlabel T9 olli, flags=MatchKeypadCollation|MatchExactly")
+        << detail << field << "6554" << (int) (QContactFilter::MatchKeypadCollation | QContactFilter::MatchExactly) << false << "g";
+
+    QTest::newRow("customlabel T9 olli, flags=MatchKeypadCollation|MatchContains")
+        << detail << field << "6554" << (int) (QContactFilter::MatchKeypadCollation | QContactFilter::MatchContains) << false << "adg";
+    
+    QTest::newRow("customlabel T9 jorma, flags=MatchKeypadCollation|MatchStartsWith")
+        << detail << field << "56762" << (int) (QContactFilter::MatchKeypadCollation | QContactFilter::MatchStartsWith) << false << "a";
+
+    QTest::newRow("customlabel T9 nen, flags=MatchKeypadCollation|MatchEndsWith")
+        << detail << field << "636" << (int) (QContactFilter::MatchKeypadCollation | QContactFilter::MatchEndsWith) << false << "b";
+}
+
+void tst_SimCM::detailFilter()
+{
+    QFETCH(QString, detailName);
+    QFETCH(QString, detailField);
+    QFETCH(QString, value);
+    QFETCH(int, flags);
+    QFETCH(bool, filterSupported);
+    QFETCH(QString, expected);
+
+    initManager("ADN");
+    QVERIFY(m_cm->error() == QContactManager::NoError);
+
+    QMap<QContactLocalId, QString> saved;
+    saved.insert(saveContact("Jorma Ollila", "123456789").localId(), "a");
+    saved.insert(saveContact("Matti Nykänen", "+123456789").localId(), "b");
+    saved.insert(saveContact("Frederik", "+123456789012").localId(), "c");
+    saved.insert(saveContact("Olli-Pekka Kallasvuo", "0718008000").localId(), "d");
+    saved.insert(saveContact("Foobar", "0987654321p").localId(), "e");
+    saved.insert(saveContact("Telco", "0718008000#1234#123").localId(), "f");
+    saved.insert(saveContact("Olli", "543253425").localId(), "g");
+    saved.insert(saveContact("Donald Duck", "313").localId(), "h");
+    saved.insert(saveContact("Daisy Duck", "0505555555").localId(), "i");
+    saved.insert(saveContact("Daisy Duck (int)", "+358505555555").localId(), "j");
+
+    QContactDetailFilter f;
+    f.setDetailDefinitionName(detailName, detailField);
+    f.setMatchFlags(QContactFilter::MatchFlags(flags));
+    f.setValue(value);
+    
+    QVERIFY(m_cm->isFilterSupported(f) == filterSupported);
+
+    QList<QContactLocalId> ids = m_cm->contactIds(f);
+    QVERIFY(m_cm->error() == QContactManager::NoError);
+
+    QString result;
+    foreach (QContactLocalId id, ids)
+        result += saved.value(id);
+
+    // Remove all 
+    m_cm->removeContacts(m_cm->contactIds(), 0);   
+
+    QCOMPARE(result, expected);
+}
+
+
+
 /*
  * Test if signals contactsAdded, contactsChanged and contactsRemoved are
  * emitted correctly.
@@ -1035,7 +1182,6 @@
     foreach(const QContact& c, contacts) {
         // Assume a valid SDN contact always has a display label and a phone number
         QVERIFY(!c.displayLabel().isEmpty());
-        QVERIFY(!c.displayLabel().contains("unnamed", Qt::CaseInsensitive));
         QVERIFY(!c.detail(QContactPhoneNumber::DefinitionName).isEmpty());
         foreach(const QContactDetail& d, c.details()) {
             qDebug() << "Detail: " << d.definitionName();
@@ -1053,6 +1199,321 @@
     QVERIFY(!cm->saveContact(&c));
 }
 
+void tst_SimCM::fillSlots()
+{
+    initManager("ADN");
+    
+    // remove all contacts
+    QList<QContactLocalId> ids = m_cm->contactIds();
+    m_cm->removeContacts(ids, 0); 
+        
+    // Update store info for empty sim card
+    TRAPD(err, getEtelStoreInfoL(KETelIccAdnPhoneBook, m_etelStoreInfoPckg));
+    QVERIFY(err == KErrNone);
+    //dumpStoreInfo();
+    
+    // Get detail definitions
+    QMap<QString, QContactDetailDefinition> defs = m_cm->detailDefinitions();
+    bool nicknameSupported = defs.contains(QContactNickname::DefinitionName);
+    bool additionalNumberSupported = !defs.value(QContactPhoneNumber::DefinitionName).isUnique();
+    bool emailSupported = defs.contains(QContactNickname::DefinitionName);
+    
+    // Fill all slots with a name
+    QList<QContact> savedContacts;
+    int i;
+    for (i=0; i<m_etelStoreInfo.iTotalEntries; i++)
+    {
+        QContact c;
+        QString label;
+        label.fill('x', 10);
+        QString tmp = QString("%1-").arg(i);
+        label.replace(0, tmp.size(), tmp);
+        QContactName name;
+        name.setCustomLabel(label);
+        c.saveDetail(&name);
+        QVERIFY(m_cm->saveContact(&c));
+        savedContacts << c;        
+    }
+    qDebug() << QString("Wrote %1 contacts with a name").arg(i);
+    
+    // Sim card should be full now. Try writing one more.
+    {
+        QContact c;
+        QContactName name;
+        name.setCustomLabel("foobar");
+        c.saveDetail(&name);
+        QVERIFY(!m_cm->saveContact(&c));
+    }
+         
+    // Write all slots with a number
+    for (i=0; i<m_etelStoreInfo.iTotalEntries; i++)
+    {
+        QContact &c = savedContacts[i];
+        QString num;
+        num.fill('0', 10);
+        QString tmp = QString("%1#").arg(i);
+        num.replace(0, tmp.size(), tmp);
+        QContactPhoneNumber number;
+        number.setNumber(num);
+        c.saveDetail(&number);
+        QVERIFY(m_cm->saveContact(&c));
+    }
+    qDebug() << QString("Wrote %1 contacts with a number").arg(i);
+    
+    // Write all slots with a nickname
+    for (i=0; i<m_etelStoreInfo.iTotalEntries && nicknameSupported; i++)
+    {
+        QContact c = savedContacts[i];
+        QContactNickname nickname;
+        QString nick;
+        nick.fill('x', 10);
+        QString tmp = QString("%1-").arg(i);
+        nick.replace(0, tmp.size(), tmp);
+        nickname.setNickname(nick);
+        c.saveDetail(&nickname);
+        if (!m_cm->saveContact(&c)) {
+            if (m_cm->error() == QContactManager::LimitReachedError)
+                break;
+            else
+                QFAIL("Failed to write nickname");
+        }
+        savedContacts[i] = c;
+    }
+    qDebug() << QString("Wrote %1 contacts with a nickname").arg(i);
+    
+    // Write all slots with a additional number
+    for (i=0; i<m_etelStoreInfo.iTotalEntries && additionalNumberSupported; i++)
+    {
+        QContact c = savedContacts[i];
+        QString num;
+        num.fill('0', 10);
+        QString tmp = QString("%1#1").arg(i);
+        num.replace(0, tmp.size(), tmp);
+        QContactPhoneNumber additionalNumber;
+        additionalNumber.setNumber(num);
+        c.saveDetail(&additionalNumber);
+        if (!m_cm->saveContact(&c)) {
+            if (m_cm->error() == QContactManager::LimitReachedError)
+                break;
+            else
+                QFAIL("Failed to write additional number");
+        }
+        savedContacts[i] = c;
+    }
+    qDebug() << QString("Wrote %1 contacts with additional number").arg(i);
+    
+    // Write all slots with a email
+    for (i=0; i<m_etelStoreInfo.iTotalEntries && emailSupported; i++)
+    {
+        QContact c = savedContacts[i];
+        QContactEmailAddress emailAddress;
+        QString email;
+        email.fill('x', 10);
+        QString tmp = QString("%1@").arg(i);
+        email.replace(0, tmp.size(), tmp);
+        emailAddress.setEmailAddress(email);
+        c.saveDetail(&emailAddress);
+        if (!m_cm->saveContact(&c)) {
+            if (m_cm->error() == QContactManager::LimitReachedError)
+                break;
+            else
+                QFAIL("Failed to write email");
+        }
+        savedContacts[i] = c;
+    }
+    qDebug() << QString("Wrote %1 contacts with an email").arg(i);
+    
+    TRAP(err, getEtelStoreInfoL(KETelIccAdnPhoneBook, m_etelStoreInfoPckg));
+    QVERIFY(err == KErrNone);
+    //dumpStoreInfo();
+
+    QList<QContact> contacts = m_cm->contacts();
+#ifdef __WINS__
+    // Cannot do full compare in emulator because of etel test server bug.
+    // If saving for example nickname fails with QContactManager::LimitReachedError
+    // it will still save the contact.
+    QVERIFY(contacts.count() == savedContacts.count());
+#else
+    QVERIFY(compareContactLists(contacts, savedContacts));
+#endif
+    QVERIFY(m_cm->removeContacts(m_cm->contactIds(), 0));
+}
+
+void tst_SimCM::sortingAdn_data()
+{
+    QTest::addColumn<QString>("definitionName");
+    QTest::addColumn<QString>("fieldName");
+    QTest::addColumn<int>("direction");
+    QTest::addColumn<int>("caseSensitivity");
+    QTest::addColumn<int>("blankPolicy");
+    QTest::addColumn<QContactIds>("expectedResult");
+    QTest::addColumn<QContactIds>("unknownResult");
+
+    initManager("ADN");
+    QContact blankName = createContact(QString(""), QString("+44752222222"));
+    QVERIFY(m_cm->saveContact(&blankName));
+    QContact james = createContact(QString("James Hunt"), QString("+44753333333"));
+    QVERIFY(m_cm->saveContact(&james));
+    QContact kimi = createContact(QString("Kimi Räikkönen"), QString("+358441111111"));
+    QVERIFY(m_cm->saveContact(&kimi));
+    QContact kimiNoCaps = createContact(QString("kimi räikkönen"), QString("+358442222222"));
+    QVERIFY(m_cm->saveContact(&kimiNoCaps));
+    QContact sebastianNoPhone = createContact(QString("Sebastian Vettel"), QString(""));
+    QVERIFY(m_cm->saveContact(&sebastianNoPhone));
+
+    QString contactNameDef = QContactName::DefinitionName;
+    QString customLabel = QContactName::FieldCustomLabel;
+    QString phoneNumberDef = QContactPhoneNumber::DefinitionName;
+    QString phoneNumber = QContactPhoneNumber::FieldNumber;
+
+    // The relative order of "kimi" and "Kimi" are undefined in this case
+    QTest::newRow("custom label, ascending, case insensitive, blanks first")
+        << contactNameDef
+        << customLabel
+        << (int) Qt::AscendingOrder
+        << (int) Qt::CaseInsensitive
+        << (int) QContactSortOrder::BlanksFirst
+        << (QContactIds()
+            << blankName.localId()
+            << james.localId()
+            << kimi.localId()
+            << kimiNoCaps.localId()
+            << sebastianNoPhone.localId())
+        << (QContactIds()
+            << kimi.localId()
+            << kimiNoCaps.localId());
+
+    QTest::newRow("custom label, ascending, case sensitive, blanks first")
+        << contactNameDef
+        << customLabel
+        << (int) Qt::AscendingOrder
+        << (int) Qt::CaseSensitive
+        << (int) QContactSortOrder::BlanksFirst
+        << (QContactIds()
+            << blankName.localId()
+            << james.localId()
+            << kimiNoCaps.localId()
+            << kimi.localId()
+            << sebastianNoPhone.localId())
+        << QContactIds();
+
+    // The relative order of "kimi" and "Kimi" are undefined in this case
+    QTest::newRow("custom label, descending, case insensitive, blanks first")
+        << contactNameDef
+        << customLabel
+        << (int) Qt::DescendingOrder
+        << (int) Qt::CaseInsensitive
+        << (int) QContactSortOrder::BlanksFirst
+        << (QContactIds()
+            << blankName.localId()
+            << sebastianNoPhone.localId()
+            << kimi.localId()
+            << kimiNoCaps.localId()
+            << james.localId())
+        << (QContactIds()
+            << kimi.localId()
+            << kimiNoCaps.localId());
+
+    QTest::newRow("phone number, ascending, case insensitive, blanks first")
+        << phoneNumberDef
+        << phoneNumber
+        << (int) Qt::AscendingOrder
+        << (int) Qt::CaseInsensitive
+        << (int) QContactSortOrder::BlanksFirst
+        << (QContactIds()
+            << sebastianNoPhone.localId()
+            << kimi.localId()
+            << kimiNoCaps.localId()
+            << blankName.localId()
+            << james.localId())
+        << QContactIds();
+
+    QTest::newRow("phone number, ascending, case sensitive, blanks last")
+        << phoneNumberDef
+        << phoneNumber
+        << (int) Qt::AscendingOrder
+        << (int) Qt::CaseSensitive
+        << (int) QContactSortOrder::BlanksLast
+        << (QContactIds()
+            << kimi.localId()
+            << kimiNoCaps.localId()
+            << blankName.localId()
+            << james.localId()
+            << sebastianNoPhone.localId())
+        << QContactIds();
+
+    QTest::newRow("phone number, descending, case insensitive, blanks first")
+        << phoneNumberDef
+        << phoneNumber
+        << (int) Qt::DescendingOrder
+        << (int) Qt::CaseInsensitive
+        << (int) QContactSortOrder::BlanksFirst
+        << (QContactIds()
+            << sebastianNoPhone.localId()
+            << james.localId()
+            << blankName.localId()
+            << kimiNoCaps.localId()
+            << kimi.localId())
+        << QContactIds();
+}
+
+/*!
+ * Contact sorting needs to be tested here, because the system tests in
+ * \tests\auto\qcontactmanagerfiltering\tst_QContactManagerFiltering are not
+ * compatible with the sim backend.
+ */
+void tst_SimCM::sortingAdn()
+{
+    QFETCH(QString, definitionName);
+    QFETCH(QString, fieldName);
+    QFETCH(int, direction);
+    QFETCH(int, caseSensitivity);
+    QFETCH(int, blankPolicy);
+    QFETCH(QContactIds, expectedResult);
+    QFETCH(QContactIds, unknownResult);
+
+    QContactSortOrder sortOrder;
+    sortOrder.setDetailDefinitionName(definitionName, fieldName);
+    sortOrder.setDirection((Qt::SortOrder) direction);
+    sortOrder.setCaseSensitivity((Qt::CaseSensitivity) caseSensitivity);
+    sortOrder.setBlankPolicy((QContactSortOrder::BlankPolicy) blankPolicy);
+    QVERIFY(sortOrder.isValid());
+
+    QList<QContactSortOrder> sortOrders;
+    sortOrders.append(sortOrder);
+
+    /*
+    TODO
+    QList<QContact> contacts(const QList<QContactSortOrder>& sortOrders = QList<QContactSortOrder>(), const QContactFetchHint& fetchHint = QContactFetchHint()) const;
+    QList<QContact> contacts(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders = QList<QContactSortOrder>(), const QContactFetchHint& fetchHint = QContactFetchHint()) const;
+    */
+
+    // First variant
+    QList<QContactLocalId> ids = m_cm->contactIds(sortOrders);
+    qDebug() << "result: " << ids;
+    qDebug() << "expected: " << expectedResult;
+    QCOMPARE(m_cm->error(), QContactManager::NoError);
+    if (unknownResult.count()) {
+        // TODO: how to check all possible alternatives?
+        foreach (QContactLocalId id, unknownResult) {
+            QVERIFY(expectedResult.removeOne(id));
+            QVERIFY(ids.removeOne(id));
+        }
+    }
+    QCOMPARE(ids, expectedResult);
+
+    // Second variant with filter
+    ids = m_cm->contactIds(QContactFilter(), sortOrders);
+    QCOMPARE(m_cm->error(), QContactManager::NoError);
+    if (unknownResult.count()) {
+        // TODO: how to check all possible alternatives?
+        foreach (QContactLocalId id, unknownResult) {
+            QVERIFY(ids.removeOne(id));
+        }
+    }
+    QCOMPARE(ids, expectedResult);
+}
+
 /*!
  * Private helper function for checking the data format that the store supports
  */
@@ -1158,11 +1619,11 @@
             contactDetail = contact.detail(detailParts[0]);
         }
 
-        // Set the field value only if not empty (do not add empty fields)  
-        if (!detailParts[2].isEmpty()) {
+        if (detailParts[2].isNull()) {
+            QVERIFY(contactDetail.setValue(detailParts[1], QString("")));
+        } else {
             QVERIFY(contactDetail.setValue(detailParts[1], detailParts[2]));
         }
-
         QVERIFY(contact.saveDetail(&contactDetail));
         parsedDetails.append(contactDetail);
     }
@@ -1171,36 +1632,45 @@
 /*
  * Private helper function for comparing QContact details to a well-known set
  * of QContactDetails.
- * \return true if all the expected contact details have a match in the \contact.
  */
-void tst_SimCM::compareDetails(QContact contact, QList<QContactDetail> expectedDetails)
+bool tst_SimCM::compareDetails(QContact contact, QList<QContactDetail> expectedDetails)
 {
-    foreach (QContactDetail expectedDetail, expectedDetails) {
-        QContactDetail actualDetail = contact.detail(expectedDetail.definitionName());
-        QVERIFY(!actualDetail.isEmpty());
+    bool result(true);
 
-        // Allow truncating the custom label to the max text length
-        if (expectedDetail.definitionName() == QContactName::DefinitionName) {
-            QContactName nameDetail = static_cast<QContactName>(expectedDetail);
-            nameDetail.setCustomLabel(nameDetail.customLabel().left(m_etelStoreInfo.iMaxTextLength));
-            expectedDetail = static_cast<QContactDetail>(nameDetail);
-        // Allow truncating the nick name to the max text length
-        } else if (expectedDetail.definitionName() == QContactNickname::DefinitionName) {
-                QContactNickname nick = static_cast<QContactNickname>(expectedDetail);
-                nick.setNickname(nick.nickname().left(m_etelStoreInfo.iMaxTextLength));
-                expectedDetail = static_cast<QContactDetail>(nick);
-        }
+    foreach (QContactDetail expectedDetail, expectedDetails) {
+        QString detailName = expectedDetail.definitionName();
+        foreach (const QString& key, expectedDetail.variantValues().keys()) {
+            bool match(false);
+            // Go through the actual details to see if the expected detail is included there
+            foreach (QContactDetail actualDetail, contact.details(expectedDetail.definitionName())) {
+                QString a = actualDetail.variantValue(key).toString();
+                QString b = expectedDetail.variantValue(key).toString();
+                if (!actualDetail.variantValues().contains(key)) {
+                    qDebug() << "field does not exist, detail:"
+                        << detailName
+                        << "key:"
+                        << key
+                        << " value: "
+                        << expectedDetail.variantValue(key);
+                } else if (actualDetail.variantValue(key) != expectedDetail.variantValue(key)) {
+                    qDebug() << "Detail found, but value does not match. Detail:"
+                        << detailName
+                        << "Expected: "
+                        << expectedDetail.variantValue(key)
+                        << " actual: "
+                        << actualDetail.variantValue(key);
+                } else {
+                    match = true;
+                }
+            }
 
-        if(!contact.details().contains(expectedDetail)) {
-            // FAIL! Make it easier to debug the output by
-            // comparing the contact detail field contents
-            foreach (const QString& key, expectedDetail.variantValues().keys()) {
-                QVariant value1 = actualDetail.value(key);
-                QVariant value2 = expectedDetail.value(key);
-                QCOMPARE(actualDetail.value(key), expectedDetail.value(key));
+            // Allow empty details, since they are not saved
+            if (!match && !expectedDetail.variantValue(key).toString().isEmpty()) {
+                result = false;
             }
         }
     }
+    return result;
 }
 
 QContact tst_SimCM::createContact(QString name, QString number)
@@ -1218,5 +1688,105 @@
     return c;
 }
 
+QContact tst_SimCM::saveContact(QString name, QString number)
+{
+    QContact c;
+    
+    QContactName n;
+    n.setCustomLabel(name);
+    c.saveDetail(&n);
+    
+    QContactPhoneNumber nb;
+    nb.setNumber(number);
+    c.saveDetail(&nb);
+    
+    if (!m_cm->saveContact(&c)) {
+        qWarning() << 
+            QString("Failed to save a contact! (name=%1,number=%2) error:%3")
+                .arg(name).arg(number).arg(m_cm->error());
+    }
+
+    return c;
+}
+
+void tst_SimCM::dumpStoreInfo()
+{
+    TPtrC name = m_etelStoreInfo.iName;
+    TPtrC8 identity = m_etelStoreInfo.iIdentity;
+    
+    qDebug() << "Store info:"
+        << "\nType                         " << m_etelStoreInfo.iType
+        << "\nTotalEntries                 " << m_etelStoreInfo.iTotalEntries
+        << "\nUsedEntries                  " << m_etelStoreInfo.iUsedEntries
+        << "\nCaps                         " << m_etelStoreInfo.iCaps
+        << "\nName                         " << QString::fromUtf16(name.Ptr(), name.Length())
+        << "\nMaxNumLength                 " << m_etelStoreInfo.iMaxNumLength
+        << "\nMaxTextLength                " << m_etelStoreInfo.iMaxTextLength
+        << "\nLocation                     " << m_etelStoreInfo.iLocation
+        << "\nChangeCounter                " << m_etelStoreInfo.iChangeCounter
+        << "\nIdentity                     " << QString::fromUtf8((const char*)identity.Ptr(), identity.Length())
+#ifdef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1
+        ;
+#else
+        << "\nMaxSecondNames               " << m_etelStoreInfo.iMaxSecondNames
+        << "\nMaxTextLengthSecondName      " << m_etelStoreInfo.iMaxTextLengthSecondName
+        << "\nMaxAdditionalNumbers         " << m_etelStoreInfo.iMaxAdditionalNumbers
+        << "\nMaxNumLengthAdditionalNumber " << m_etelStoreInfo.iMaxNumLengthAdditionalNumber
+        << "\nMaxTextLengthAdditionalNumber" << m_etelStoreInfo.iMaxTextLengthAdditionalNumber
+        << "\nMaxGroupNames                " << m_etelStoreInfo.iMaxGroupNames
+        << "\nMaxTextLengthGroupName       " << m_etelStoreInfo.iMaxTextLengthGroupName
+        << "\nMaxEmailAddr                 " << m_etelStoreInfo.iMaxEmailAddr
+        << "\nMaxTextLengthEmailAddr       " << m_etelStoreInfo.iMaxTextLengthEmailAddr;    
+#endif
+}
+
+bool tst_SimCM::compareContactLists(QList<QContact> lista, QList<QContact> listb)
+{
+    // NOTE: This compare is contact order insensitive.  
+    
+    // Remove matching contacts
+    foreach (QContact a, lista) {
+        foreach (QContact b, listb) {
+            if (compareContacts(a, b)) {
+                lista.removeOne(a);
+                listb.removeOne(b);
+                break;
+            }
+        }
+    }
+    
+    //if (lista.count() != 0) qDebug() << "\nList A:\n" << lista;
+    //if (listb.count() != 0) qDebug() << "\nList B:\n" << listb;
+        
+    return (lista.count() == 0 && listb.count() == 0);
+}
+
+bool tst_SimCM::compareContacts(QContact ca, QContact cb)
+{
+    // NOTE: This compare is contact detail order insensitive.
+    
+    if (ca.localId() != cb.localId())
+        return false;
+    
+    QList<QContactDetail> aDetails = ca.details();
+    QList<QContactDetail> bDetails = cb.details();
+
+    // Remove matching details
+    foreach (QContactDetail ad, aDetails) {
+        foreach (QContactDetail bd, bDetails) {
+            if (ad == bd) {
+                ca.removeDetail(&ad);
+                cb.removeDetail(&bd);
+                break;
+            }
+        }
+    }
+    
+    if (ca != cb)
+        qDebug() << "\nCompare failed:\n" << "A:\n" << ca << "\nB:\n" << cb;
+    
+    return (ca == cb);
+}
+
 QTEST_MAIN(tst_SimCM)
 #include "tst_simcm.moc"