diff options
Diffstat (limited to 'tests/auto/corelib/platform')
11 files changed, 595 insertions, 8 deletions
diff --git a/tests/auto/corelib/platform/CMakeLists.txt b/tests/auto/corelib/platform/CMakeLists.txt index 92462e98d8..3455736eab 100644 --- a/tests/auto/corelib/platform/CMakeLists.txt +++ b/tests/auto/corelib/platform/CMakeLists.txt @@ -1,3 +1,10 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + if(ANDROID) add_subdirectory(android) + add_subdirectory(android_appless) +endif() +if(WIN32) + add_subdirectory(windows) endif() diff --git a/tests/auto/corelib/platform/android/CMakeLists.txt b/tests/auto/corelib/platform/android/CMakeLists.txt index c2749825ac..6b7e9b2901 100644 --- a/tests/auto/corelib/platform/android/CMakeLists.txt +++ b/tests/auto/corelib/platform/android/CMakeLists.txt @@ -1,21 +1,28 @@ -# Generated from android.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_android Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_android LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_android SOURCES tst_android.cpp + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::Widgets ) if(ANDROID) set_property(TARGET tst_android APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/testdata ) - # QTBUG-88840 # special case - qt_android_generate_deployment_settings(tst_android) # special case endif() - -#### Keys ignored in scope 1:.:.:android.pro:<TRUE>: -# DISTFILES = "testdata/assets/test.txt" diff --git a/tests/auto/corelib/platform/android/testdata/assets/top_level_dir/file_in_top_dir.txt b/tests/auto/corelib/platform/android/testdata/assets/top_level_dir/file_in_top_dir.txt new file mode 100644 index 0000000000..61e2c47c25 --- /dev/null +++ b/tests/auto/corelib/platform/android/testdata/assets/top_level_dir/file_in_top_dir.txt @@ -0,0 +1 @@ +FooBar
\ No newline at end of file diff --git a/tests/auto/corelib/platform/android/testdata/assets/top_level_dir/sub_dir/file_in_sub_dir.txt b/tests/auto/corelib/platform/android/testdata/assets/top_level_dir/sub_dir/file_in_sub_dir.txt new file mode 100644 index 0000000000..61e2c47c25 --- /dev/null +++ b/tests/auto/corelib/platform/android/testdata/assets/top_level_dir/sub_dir/file_in_sub_dir.txt @@ -0,0 +1 @@ +FooBar
\ No newline at end of file diff --git a/tests/auto/corelib/platform/android/testdata/assets/top_level_dir/sub_dir/sub_dir_2/sub_dir_3/file_in_sub_dir_3.txt b/tests/auto/corelib/platform/android/testdata/assets/top_level_dir/sub_dir/sub_dir_2/sub_dir_3/file_in_sub_dir_3.txt new file mode 100644 index 0000000000..61e2c47c25 --- /dev/null +++ b/tests/auto/corelib/platform/android/testdata/assets/top_level_dir/sub_dir/sub_dir_2/sub_dir_3/file_in_sub_dir_3.txt @@ -0,0 +1 @@ +FooBar
\ No newline at end of file diff --git a/tests/auto/corelib/platform/android/tst_android.cpp b/tests/auto/corelib/platform/android/tst_android.cpp index 2fa6ec6415..76811a31ad 100644 --- a/tests/auto/corelib/platform/android/tst_android.cpp +++ b/tests/auto/corelib/platform/android/tst_android.cpp @@ -1,11 +1,21 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <jni.h> #include <QTest> +#include <QGuiApplication> #include <QtCore/qnativeinterface.h> #include <QtCore/qjniobject.h> +#include <QtCore/qdiriterator.h> +#include <QScreen> +#include <qpa/qplatformscreen.h> +#include <qpa/qplatformnativeinterface.h> +#include <QtCore/qdiriterator.h> +#include <QWidget> +#include <QSignalSpy> + +using namespace Qt::StringLiterals; class tst_Android : public QObject { @@ -13,9 +23,13 @@ Q_OBJECT private slots: void assetsRead(); void assetsNotWritable(); + void assetsIterating(); void testAndroidSdkVersion(); void testAndroidActivity(); void testRunOnAndroidMainThread(); + void testFullScreenDimensions(); + void orientationChange_data(); + void orientationChange(); }; void tst_Android::assetsRead() @@ -41,6 +55,31 @@ void tst_Android::assetsNotWritable() QVERIFY(!file.open(QIODevice::Append)); } +void tst_Android::assetsIterating() +{ + QStringList assets = {"assets:/top_level_dir/file_in_top_dir.txt", + "assets:/top_level_dir/sub_dir", + "assets:/top_level_dir/sub_dir/file_in_sub_dir.txt", + "assets:/top_level_dir/sub_dir/sub_dir_2", + "assets:/top_level_dir/sub_dir/sub_dir_2/sub_dir_3", + "assets:/top_level_dir/sub_dir/sub_dir_2/sub_dir_3/file_in_sub_dir_3.txt"}; + + // Note that we have an "assets:/top_level_dir/sub_dir/empty_sub_dir" in the test's + // assets physical directory, but empty folders are not packaged in the built apk, + // so it's expected to not have such folder be listed in the assets on runtime + + QDirIterator it("assets:/top_level_dir", QDirIterator::Subdirectories); + QStringList iteratorAssets; + while (it.hasNext()) + iteratorAssets.append(it.next()); + + QVERIFY(assets == iteratorAssets); + + auto entryList = QDir{"assets:/"_L1}.entryList(QStringList{"*.txt"_L1}); + QCOMPARE(entryList.size(), 1); + QCOMPARE(entryList[0], "test.txt"_L1); +} + void tst_Android::testAndroidSdkVersion() { QVERIFY(QNativeInterface::QAndroidApplication::sdkVersion() > 0); @@ -164,6 +203,175 @@ void tst_Android::testRunOnAndroidMainThread() } } +Q_DECLARE_JNI_CLASS(QtActivityDelegateBase, "org/qtproject/qt/android/QtActivityDelegateBase") + +void setSystemUiVisibility(int visibility) +{ + QNativeInterface::QAndroidApplication::runOnAndroidMainThread([visibility] { + auto context = QNativeInterface::QAndroidApplication::context(); + auto activityDelegate = context.callMethod<QtJniTypes::QtActivityDelegateBase>("getActivityDelegate"); + activityDelegate.callMethod<void>("setSystemUiVisibility", jint(visibility)); + }).waitForFinished(); +} + +// QTBUG-107604 +void tst_Android::testFullScreenDimensions() +{ + static int SYSTEM_UI_VISIBILITY_NORMAL = 0; + static int SYSTEM_UI_VISIBILITY_FULLSCREEN = 1; + static int SYSTEM_UI_VISIBILITY_TRANSLUCENT = 2; + + // this will trigger new layout updates + setSystemUiVisibility(SYSTEM_UI_VISIBILITY_FULLSCREEN); + setSystemUiVisibility(SYSTEM_UI_VISIBILITY_NORMAL); + + QJniObject activity = QNativeInterface::QAndroidApplication::context(); + QVERIFY(activity.isValid()); + + QJniObject windowManager = + activity.callObjectMethod("getWindowManager", "()Landroid/view/WindowManager;"); + QVERIFY(windowManager.isValid()); + + QJniObject display = windowManager.callObjectMethod("getDefaultDisplay", "()Landroid/view/Display;"); + QVERIFY(display.isValid()); + + QJniObject appSize("android/graphics/Point"); + QVERIFY(appSize.isValid()); + + display.callMethod<void>("getSize", "(Landroid/graphics/Point;)V", appSize.object()); + + QJniObject realSize("android/graphics/Point"); + QVERIFY(realSize.isValid()); + + display.callMethod<void>("getRealSize", "(Landroid/graphics/Point;)V", realSize.object()); + + QPlatformScreen *screen = QGuiApplication::primaryScreen()->handle(); + + { + // Normal - + // available geometry == app size (system bars visible and removed from available geometry) + QCoreApplication::processEvents(); + QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); + QVERIFY(window.isValid()); + + QJniObject decorView = window.callObjectMethod("getDecorView", "()Landroid/view/View;"); + QVERIFY(decorView.isValid()); + + QJniObject insets = + decorView.callObjectMethod("getRootWindowInsets", "()Landroid/view/WindowInsets;"); + QVERIFY(insets.isValid()); + + int insetsWidth = insets.callMethod<jint>("getSystemWindowInsetRight") + + insets.callMethod<jint>("getSystemWindowInsetLeft"); + + int insetsHeight = insets.callMethod<jint>("getSystemWindowInsetTop") + + insets.callMethod<jint>("getSystemWindowInsetBottom"); + + QTRY_COMPARE(screen->availableGeometry().width(), + int(appSize.getField<jint>("x")) - insetsWidth); + QTRY_COMPARE(screen->availableGeometry().height(), + int(appSize.getField<jint>("y")) - insetsHeight); + + QTRY_COMPARE(screen->geometry().width(), int(realSize.getField<jint>("x"))); + QTRY_COMPARE(screen->geometry().height(), int(realSize.getField<jint>("y"))); + } + + { + setSystemUiVisibility(SYSTEM_UI_VISIBILITY_FULLSCREEN); + + // Fullscreen + // available geometry == full display size (system bars hidden) + QCoreApplication::processEvents(); + QTRY_COMPARE(screen->availableGeometry().width(), int(realSize.getField<jint>("x"))); + QTRY_COMPARE(screen->availableGeometry().height(), int(realSize.getField<jint>("y"))); + + QTRY_COMPARE(screen->geometry().width(), int(realSize.getField<jint>("x"))); + QTRY_COMPARE(screen->geometry().height(), int(realSize.getField<jint>("y"))); + } + + { + setSystemUiVisibility(SYSTEM_UI_VISIBILITY_TRANSLUCENT); + + // Translucent + // available geometry == full display size (system bars visible but drawable under) + QCoreApplication::processEvents(); + QTRY_COMPARE(screen->availableGeometry().width(), int(realSize.getField<jint>("x"))); + QTRY_COMPARE(screen->availableGeometry().height(), int(realSize.getField<jint>("y"))); + + QTRY_COMPARE(screen->geometry().width(), int(realSize.getField<jint>("x"))); + QTRY_COMPARE(screen->geometry().height(), int(realSize.getField<jint>("y"))); + } +} + +void tst_Android::orientationChange_data() +{ + QTest::addColumn<int>("nativeOrientation"); + QTest::addColumn<Qt::ScreenOrientation>("expected"); + QTest::addColumn<QSize>("screenSize"); + + const QSize portraitSize = QGuiApplication::primaryScreen()->size(); + const QSize landscapeSize = QSize(portraitSize.height(), portraitSize.width()); + + // Rotations without 180 degree or inverted portrait, assuming that the device is in portrait + // position. These are ok for Android 6(API 23), 8 (API 27) and 14 (API 34) + QTest::newRow("InvertedLandscape") << 8 << Qt::InvertedLandscapeOrientation << landscapeSize; + QTest::newRow("Portrait") << 1 << Qt::PortraitOrientation << portraitSize; + QTest::newRow("Landscape") << 0 << Qt::LandscapeOrientation << landscapeSize; + QTest::newRow("Portrait2") << 1 << Qt::PortraitOrientation << portraitSize; + + // Rotations over inverted portrait doing only 90 degree turns. + QTest::newRow("InvertedLandscape2") << 8 << Qt::InvertedLandscapeOrientation << landscapeSize; + QTest::newRow("InvertedPortrait") << 9 << Qt::InvertedPortraitOrientation << portraitSize; + QTest::newRow("Landscape2") << 0 << Qt::LandscapeOrientation << landscapeSize; + QTest::newRow("InvertedPortrait2") << 9 << Qt::InvertedPortraitOrientation << portraitSize; + QTest::newRow("InvertedLandscape3") << 8 << Qt::InvertedLandscapeOrientation << landscapeSize; + + // Rotations with 180 degree turns. + // Android 6 (API23) Does not understand these transitions. + if (QNativeInterface::QAndroidApplication::sdkVersion() > __ANDROID_API_M__) { + QTest::newRow("Landscape3") << 0 << Qt::LandscapeOrientation << landscapeSize; + QTest::newRow("InvertedLandscape4") + << 8 << Qt::InvertedLandscapeOrientation << landscapeSize; + QTest::newRow("Portrait3") << 1 << Qt::PortraitOrientation << portraitSize; + } else { + qWarning() << "180 degree turn rotation test cases are not run on Android 6 (API 23) and " + "below."; + } + // Android 8 (API 27) does not understand portrait-'inverted portrait'-portrait transition. + if (QNativeInterface::QAndroidApplication::sdkVersion() > __ANDROID_API_O_MR1__) { + QTest::newRow("InvertedPortrait3") << 9 << Qt::InvertedPortraitOrientation << portraitSize; + QTest::newRow("Portrait4") << 1 << Qt::PortraitOrientation << portraitSize; + } else { + qWarning() << "Portrait-'Inverted portrait'-Portrait rotation test cases are not run on " + "Android 8 (API 27) and below."; + } +} + +void tst_Android::orientationChange() +{ + QFETCH(int, nativeOrientation); + QFETCH(Qt::ScreenOrientation, expected); + QFETCH(QSize, screenSize); + + if (QNativeInterface::QAndroidApplication::sdkVersion() == __ANDROID_API_P__) + QSKIP("Android 9 orientation changes callbacks are buggy (QTBUG-124890)."); + + // For QTBUG-94459 to check that the widget size are consistent after orientation changes + QWidget widget; + widget.show(); + + QScreen *screen = QGuiApplication::primaryScreen(); + QSignalSpy orientationSpy(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation))); + + auto context = QNativeInterface::QAndroidApplication::context(); + context.callMethod<void>("setRequestedOrientation", nativeOrientation); + + orientationSpy.wait(); + QTRY_COMPARE(screen->orientation(), expected); + QCOMPARE(orientationSpy.size(), 1); + QCOMPARE(screen->size(), screenSize); + QCOMPARE(widget.size(), screen->availableSize()); +} + QTEST_MAIN(tst_Android) #include "tst_android.moc" - diff --git a/tests/auto/corelib/platform/android_appless/CMakeLists.txt b/tests/auto/corelib/platform/android_appless/CMakeLists.txt new file mode 100644 index 0000000000..bcb7024b29 --- /dev/null +++ b/tests/auto/corelib/platform/android_appless/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_android_appless Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_android_appless LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_android_appless + SOURCES + tst_android_appless.cpp + LIBRARIES + Qt::Gui +) diff --git a/tests/auto/corelib/platform/android_appless/tst_android_appless.cpp b/tests/auto/corelib/platform/android_appless/tst_android_appless.cpp new file mode 100644 index 0000000000..cb7ad8875c --- /dev/null +++ b/tests/auto/corelib/platform/android_appless/tst_android_appless.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QTimer> +#include <QGuiApplication> +#include <QWindow> + +using namespace Qt::StringLiterals; + +class tst_AndroidAppless : public QObject +{ + Q_OBJECT +private slots: + void app_data(); + void app(); + +}; + +#define CREATE_DUMMY_ARGC_ARGV() \ + int argc = 1; \ + char *argv[] = { const_cast<char *>("tst_android_appless") }; + + +void tst_AndroidAppless::app_data() +{ + QTest::addColumn<QString>("displayName"); + QTest::addRow("one") << "The first QGuiApplication instance"; + QTest::addRow("two") << "The second QGuiApplication instance"; +} + +void tst_AndroidAppless::app() +{ + QFETCH(const QString, displayName); + CREATE_DUMMY_ARGC_ARGV() + + QGuiApplication app(argc, argv); + app.setApplicationDisplayName(displayName); + + QWindow window; + window.show(); + + QTimer::singleShot(1000, &app, QGuiApplication::quit); + + window.show(); + app.exec(); +} + +#undef CREATE_DUMMY_ARGC_ARGV + +QTEST_APPLESS_MAIN(tst_AndroidAppless) +#include "tst_android_appless.moc" + diff --git a/tests/auto/corelib/platform/windows/CMakeLists.txt b/tests/auto/corelib/platform/windows/CMakeLists.txt new file mode 100644 index 0000000000..24b2a69a0e --- /dev/null +++ b/tests/auto/corelib/platform/windows/CMakeLists.txt @@ -0,0 +1,4 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +add_subdirectory(qcomobject) diff --git a/tests/auto/corelib/platform/windows/qcomobject/CMakeLists.txt b/tests/auto/corelib/platform/windows/qcomobject/CMakeLists.txt new file mode 100644 index 0000000000..9cdd6b57bc --- /dev/null +++ b/tests/auto/corelib/platform/windows/qcomobject/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qcomobject Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcomobject LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qcomobject + SOURCES + tst_qcomobject.cpp + LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/platform/windows/qcomobject/tst_qcomobject.cpp b/tests/auto/corelib/platform/windows/qcomobject/tst_qcomobject.cpp new file mode 100644 index 0000000000..3c609238fc --- /dev/null +++ b/tests/auto/corelib/platform/windows/qcomobject/tst_qcomobject.cpp @@ -0,0 +1,267 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> + +#ifdef Q_OS_WIN + +# include <private/qcomobject_p.h> + +# include <wrl/client.h> + +using Microsoft::WRL::ComPtr; + +QT_BEGIN_NAMESPACE + +template <typename T, typename... Args> +ComPtr<T> makeComObject(Args &&...args) +{ + ComPtr<T> p; + // Don't use Attach because of MINGW64 bug + // #892 Microsoft::WRL::ComPtr::Attach leaks references + *p.GetAddressOf() = new T(std::forward<Args>(args)...); + return p; +} + +MIDL_INTERFACE("878fab04-7da0-41ea-9c49-058c7fa0d80a") +IIntermediate : public IUnknown{}; + +MIDL_INTERFACE("65a29ce9-191c-4182-9185-06dd70aafc5d") +IDirect : public IIntermediate{}; + +class ComImplementation : public QComObject<IDirect> +{ +}; + +MIDL_INTERFACE("d05397e0-da7f-4055-8563-a5b80f095e6c") +IMultipleA : public IUnknown{}; + +MIDL_INTERFACE("67e298c5-ec5f-4c45-a779-bfba2484e142") +IMultipleB : public IUnknown{}; + +class MultipleComImplementation : public QComObject<IMultipleA, IMultipleB> +{ +}; + +MIDL_INTERFACE("b8278a1b-0c3b-4bbd-99db-1e8a141483fa") +IOther : public IUnknown{}; + +# ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IIntermediate, 0x878fab04, 0x7da0, 0x41ea, 0x9c, 0x49, 0x05, 0x8c, 0x7f, 0xa0, 0xd8, + 0x0a) +__CRT_UUID_DECL(IDirect, 0x65a29ce9, 0x191c, 0x4182, 0x91, 0x85, 0x06, 0xdd, 0x70, 0xaa, 0xfc, 0x5d) +__CRT_UUID_DECL(IMultipleA, 0xd05397e0, 0xda7f, 0x4055, 0x85, 0x63, 0xa5, 0xb8, 0x0f, 0x09, 0x5e, + 0x6c) +__CRT_UUID_DECL(IMultipleB, 0x67e298c5, 0xec5f, 0x4c45, 0xa7, 0x79, 0xbf, 0xba, 0x24, 0x84, 0xe1, + 0x42) +__CRT_UUID_DECL(IOther, 0xb8278a1b, 0x0c3b, 0x4bbd, 0x99, 0xdb, 0x1e, 0x8a, 0x14, 0x14, 0x83, 0xfa) +# endif + +namespace QtPrivate { + +template <> +struct QComObjectTraits<IDirect> +{ + static constexpr bool isGuidOf(REFIID riid) noexcept + { + return QComObjectTraits<IDirect, IIntermediate>::isGuidOf(riid); + } +}; + +} // namespace QtPrivate +QT_END_NAMESPACE + +class tst_QComObject : public QObject +{ + Q_OBJECT +private slots: + void QueryInterface_returnsConvertedPointer_whenIUnknownIsRequested(); + void QueryInterface_returnsConvertedPointer_whenDirectParentIsRequested(); + void QueryInterface_returnsConvertedPointer_whenDirectIntermediateIsRequested(); + void QueryInterface_returnsConvertedPointer_whenIUnknownOfMultipleParentsIsRequested(); + void QueryInterface_returnsConvertedPointer_whenFirstOfMultipleParentsIsRequested(); + void QueryInterface_returnsConvertedPointer_whenSecondOfMultipleParentsIsRequested(); + void QueryInterface_returnsNullPointer_whenNonParentIsRequested(); + void QueryInterface_returnsNullPointer_whenNullPointerIsPassedForReceivingObject(); + void QueryInterface_incrementsReferenceCount_whenConvertedPointerIsReturned(); + void AddRef_incrementsReferenceCountByOne(); + void Release_decrementsReferenceCountByOne(); +}; + +void tst_QComObject::QueryInterface_returnsConvertedPointer_whenIUnknownIsRequested() +{ + // Arrange + const ComPtr<ComImplementation> implementation = makeComObject<ComImplementation>(); + + ComPtr<IUnknown> unknown; + + // Act + const HRESULT queryResult = implementation->QueryInterface(__uuidof(IUnknown), &unknown); + + // Assert + QCOMPARE(queryResult, S_OK); + QCOMPARE(unknown.Get(), implementation.Get()); +} + +void tst_QComObject::QueryInterface_returnsConvertedPointer_whenDirectParentIsRequested() +{ + // Arrange + const ComPtr<ComImplementation> implementation = makeComObject<ComImplementation>(); + + ComPtr<IDirect> direct; + + // Act + const HRESULT queryResult = implementation->QueryInterface(__uuidof(IDirect), &direct); + + // Assert + QCOMPARE(queryResult, S_OK); + QCOMPARE(direct.Get(), implementation.Get()); +} + +void tst_QComObject::QueryInterface_returnsConvertedPointer_whenDirectIntermediateIsRequested() +{ + // Arrange + const ComPtr<ComImplementation> implementation = makeComObject<ComImplementation>(); + + ComPtr<IIntermediate> intermediate; + + // Act + const HRESULT queryResult = + implementation->QueryInterface(__uuidof(IIntermediate), &intermediate); + + // Assert + QCOMPARE(queryResult, S_OK); + QCOMPARE(intermediate.Get(), implementation.Get()); +} + +void tst_QComObject:: + QueryInterface_returnsConvertedPointer_whenIUnknownOfMultipleParentsIsRequested() +{ + // Arrange + const ComPtr<MultipleComImplementation> implementation = + makeComObject<MultipleComImplementation>(); + + ComPtr<IUnknown> unknown; + + // Act + const HRESULT queryResult = implementation->QueryInterface(__uuidof(IUnknown), &unknown); + + // Assert + QCOMPARE(queryResult, S_OK); + + // Cast MultipleComImplementation to IMultipleA to prevent ambiguity + QCOMPARE(unknown.Get(), static_cast<IMultipleA *>(implementation.Get())); +} + +void tst_QComObject::QueryInterface_returnsConvertedPointer_whenFirstOfMultipleParentsIsRequested() +{ + // Arrange + const ComPtr<MultipleComImplementation> implementation = + makeComObject<MultipleComImplementation>(); + + ComPtr<IMultipleA> multiple; + + // Act + const HRESULT queryResult = implementation->QueryInterface(__uuidof(IMultipleA), &multiple); + + // Assert + QCOMPARE(queryResult, S_OK); + QCOMPARE(multiple.Get(), implementation.Get()); +} + +void tst_QComObject::QueryInterface_returnsConvertedPointer_whenSecondOfMultipleParentsIsRequested() +{ + // Arrange + const ComPtr<MultipleComImplementation> implementation = + makeComObject<MultipleComImplementation>(); + + ComPtr<IMultipleB> multiple; + + // Act + const HRESULT queryResult = implementation->QueryInterface(__uuidof(IMultipleB), &multiple); + + // Assert + QCOMPARE(queryResult, S_OK); + QCOMPARE(multiple.Get(), implementation.Get()); +} + +void tst_QComObject::QueryInterface_returnsNullPointer_whenNonParentIsRequested() +{ + // Arrange + const ComPtr<ComImplementation> implementation = makeComObject<ComImplementation>(); + + ComPtr<IOther> other; + + // Act + const HRESULT queryResult = implementation->QueryInterface(__uuidof(IOther), &other); + + // Assert + QCOMPARE(queryResult, E_NOINTERFACE); + QCOMPARE(other.Get(), nullptr); +} + +void tst_QComObject::QueryInterface_returnsNullPointer_whenNullPointerIsPassedForReceivingObject() +{ + // Arrange + const ComPtr<ComImplementation> implementation = makeComObject<ComImplementation>(); + + // Act + const HRESULT queryResult = implementation->QueryInterface(__uuidof(IUnknown), nullptr); + + // Assert + QCOMPARE(queryResult, E_POINTER); +} + +void tst_QComObject::QueryInterface_incrementsReferenceCount_whenConvertedPointerIsReturned() +{ + // Arrange + const ComPtr<ComImplementation> implementation = makeComObject<ComImplementation>(); + + ComPtr<IUnknown> unknown; + + // Act + implementation->QueryInterface(__uuidof(IUnknown), &unknown); + + // As there's no any way to get the current reference count of an object, just add one more + // reference and assert against cumulative reference count value + const ULONG referenceCount = implementation->AddRef(); + + // Assert + QCOMPARE(referenceCount, 3); +} + +void tst_QComObject::AddRef_incrementsReferenceCountByOne() +{ + // Arrange + const ComPtr<ComImplementation> implementation = makeComObject<ComImplementation>(); + + // Act + const ULONG referenceCount1 = implementation->AddRef(); + const ULONG referenceCount2 = implementation->AddRef(); + + // Assert + QCOMPARE(referenceCount1, 2); + QCOMPARE(referenceCount2, 3); +} + +void tst_QComObject::Release_decrementsReferenceCountByOne() +{ + // Arrange + const ComPtr<ComImplementation> implementation = makeComObject<ComImplementation>(); + + implementation->AddRef(); + implementation->AddRef(); + + // Act + const ULONG referenceCount1 = implementation->Release(); + const ULONG referenceCount2 = implementation->Release(); + + // Assert + QCOMPARE(referenceCount1, 2); + QCOMPARE(referenceCount2, 1); +} + +QTEST_MAIN(tst_QComObject) +# include "tst_qcomobject.moc" + +#endif // Q_OS_WIN |