Select appropriate version for located module components
authorMatthew Vogt <matthew.vogt@nokia.com>
Fri, 13 Jul 2012 05:34:05 +0000 (15:34 +1000)
committerQt by Nokia <qt-info@nokia.com>
Tue, 17 Jul 2012 01:52:04 +0000 (03:52 +0200)
When a located module is imported with a version specifier, ensure that
the components resolved from that module use the appropriate version.

Task-number: QTBUG-26473
Change-Id: I33209ddef3fe9bb0ab9d096dfe19aff233744afc
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
src/qml/qml/qqmlimport.cpp
tests/auto/qml/qqmlmoduleplugin/data/localModule/TestComponent.1.0.qml [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/data/localModule/TestComponent.1.1.qml [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/data/localModule/TestComponent.2.0.qml [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/data/localModule/qmldir [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp

index 00ea8c5..55c07ac 100644 (file)
@@ -508,41 +508,43 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
         }
     }
 
-    bool typeWasDeclaredInQmldir = false;
-    if (!qmlDirComponents.isEmpty()) {
-        QQmlDirComponents::ConstIterator it = qmlDirComponents.find(type);
-        if (it != qmlDirComponents.end()) {
-            typeWasDeclaredInQmldir = true;
-            // first found is last inserted - process in reverse
-            QQmlDirComponents::ConstIterator begin = it;
-            while (++it != qmlDirComponents.end() && it.key() == type) {}
-            do {
-                --it;
-                const QQmlDirParser::Component &c = *it;
-
-                // importing version -1 means import ALL versions
-                if ((majversion == -1) || (c.majorVersion == majversion &&
-                                           minversion >= c.minorVersion)) {
-
-                    QString candidate = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
+    QQmlDirComponents::ConstIterator it = qmlDirComponents.find(type), end = qmlDirComponents.end();
+    if (it != end) {
+        QString componentUrl;
+        QQmlDirComponents::ConstIterator candidate = end;
+        for ( ; it != end && it.key() == type; ++it) {
+            const QQmlDirParser::Component &c = *it;
+
+            // importing version -1 means import ALL versions
+            if ((majversion == -1) ||
+                (c.majorVersion == majversion && c.minorVersion <= minversion)) {
+                // Is this better than the previous candidate?
+                if ((candidate == end) ||
+                    (c.majorVersion > candidate->majorVersion) ||
+                    ((c.majorVersion == candidate->majorVersion) && (c.minorVersion > candidate->minorVersion))) {
+                    componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName);
                     if (c.internal && base) {
-                        if (resolveLocalUrl(*base, c.fileName) != candidate)
+                        if (resolveLocalUrl(*base, c.fileName) != componentUrl)
                             continue; // failed attempt to access an internal type
                     }
-                    if (base && *base == candidate) {
+                    if (base && (*base == componentUrl)) {
                         if (typeRecursionDetected)
                             *typeRecursionDetected = true;
                         continue; // no recursion
                     }
-                    if (url_return)
-                        *url_return = candidate;
-                    return true;
+
+                    // This is our best candidate so far
+                    candidate = it;
                 }
-            } while (it != begin);
+            }
         }
-    }
 
-    if (!typeWasDeclaredInQmldir && !isLibrary) {
+        if (candidate != end) {
+            if (url_return)
+                *url_return = componentUrl;
+            return true;
+        }
+    } else if (!isLibrary) {
         QString qmlUrl = url + QString::fromRawData(type.constData(), type.length()) + dotqml_string;
 
         bool exists = false;
@@ -554,7 +556,7 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
         }
 
         if (exists) {
-            if (base && *base == qmlUrl) { // no recursion
+            if (base && (*base == qmlUrl)) { // no recursion
                 if (typeRecursionDetected)
                     *typeRecursionDetected = true;
             } else {
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/localModule/TestComponent.1.0.qml b/tests/auto/qml/qqmlmoduleplugin/data/localModule/TestComponent.1.0.qml
new file mode 100644 (file)
index 0000000..ad757fd
--- /dev/null
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Item {
+    property int majorVersion: 1
+    property int minorVersion: 0
+}
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/localModule/TestComponent.1.1.qml b/tests/auto/qml/qqmlmoduleplugin/data/localModule/TestComponent.1.1.qml
new file mode 100644 (file)
index 0000000..531912b
--- /dev/null
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Item {
+    property int majorVersion: 1
+    property int minorVersion: 1
+}
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/localModule/TestComponent.2.0.qml b/tests/auto/qml/qqmlmoduleplugin/data/localModule/TestComponent.2.0.qml
new file mode 100644 (file)
index 0000000..74c9948
--- /dev/null
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Item {
+    property int majorVersion: 2
+    property int minorVersion: 0
+}
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/localModule/qmldir b/tests/auto/qml/qqmlmoduleplugin/data/localModule/qmldir
new file mode 100644 (file)
index 0000000..052e2d9
--- /dev/null
@@ -0,0 +1,3 @@
+TestComponent 1.0 TestComponent.1.0.qml
+TestComponent 1.1 TestComponent.1.1.qml
+TestComponent 2.0 TestComponent.2.0.qml
index b649075..67b3d00 100644 (file)
@@ -72,6 +72,8 @@ private slots:
     void implicitQmldir_data();
     void importsNested();
     void importsNested_data();
+    void importLocalModule();
+    void importLocalModule_data();
 
 private:
     QString m_importsDirectory;
@@ -395,6 +397,55 @@ void tst_qqmlmoduleplugin::importsNested()
     }
 }
 
+void tst_qqmlmoduleplugin::importLocalModule()
+{
+    QFETCH(QString, qml);
+    QFETCH(int, majorVersion);
+    QFETCH(int, minorVersion);
+
+    QQmlEngine engine;
+    QQmlComponent component(&engine);
+    component.setData(qml.toUtf8(), testFileUrl("empty.qml"));
+
+    QScopedPointer<QObject> object(component.create());
+    QVERIFY(object != 0);
+    QCOMPARE(object->property("majorVersion").value<int>(), majorVersion);
+    QCOMPARE(object->property("minorVersion").value<int>(), minorVersion);
+}
+
+void tst_qqmlmoduleplugin::importLocalModule_data()
+{
+    QTest::addColumn<QString>("qml");
+    QTest::addColumn<int>("majorVersion");
+    QTest::addColumn<int>("minorVersion");
+
+    QTest::newRow("default version")
+        << "import \"localModule\"\n"
+           "TestComponent {}"
+        << 2 << 0;
+
+    QTest::newRow("specific version")
+        << "import \"localModule\" 1.1\n"
+           "TestComponent {}"
+        << 1 << 1;
+
+    QTest::newRow("lesser version")
+        << "import \"localModule\" 1.0\n"
+           "TestComponent {}"
+        << 1 << 0;
+
+    // Note: this does not match the behaviour of installed modules, which fail for this case:
+    QTest::newRow("nonexistent version")
+        << "import \"localModule\" 1.3\n"
+           "TestComponent {}"
+        << 1 << 1;
+
+    QTest::newRow("high version")
+        << "import \"localModule\" 2.0\n"
+           "TestComponent {}"
+        << 2 << 0;
+}
+
 QTEST_MAIN(tst_qqmlmoduleplugin)
 
 #include "tst_qqmlmoduleplugin.moc"