Simplify VARIANT handling using a QComVariant RAII wrapper
The QComVariant RAII wrapper automates lifetime management of VARIANT structures and elements that it contains. For example, it will call IUnknown::AddRef if a COM interface is assigned to it. It will also clear all held resources at destruction. Task-number: QTBUG-126530 Change-Id: I543d236293d25cbc70ff25046e553351eccfb850 Reviewed-by: Oliver Wolff <oliver.wolff@qt.io> (cherry picked from commit 717580c1985dd0804363bafb8822f82dacfb3f90) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
51f7513bc0
commit
267e378d14
@ -563,6 +563,7 @@ qt_internal_extend_target(Core CONDITION WIN32
|
||||
platform/windows/qcomobject_p.h
|
||||
platform/windows/qcomptr_p.h
|
||||
platform/windows/qbstr_p.h
|
||||
platform/windows/qcomvariant_p.h
|
||||
LIBRARIES
|
||||
advapi32
|
||||
authz
|
||||
|
134
src/corelib/platform/windows/qcomvariant_p.h
Normal file
134
src/corelib/platform/windows/qcomvariant_p.h
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef QCOMVARIANT_P_H
|
||||
#define QCOMVARIANT_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_QDOC)
|
||||
|
||||
// clang-format off
|
||||
#include <QtCore/private/qglobal_p.h>
|
||||
#include <QtCore/qt_windows.h>
|
||||
#include <QtCore/private/qbstr_p.h>
|
||||
#include <QtCore/private/qcomptr_p.h>
|
||||
// clang-format on
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
struct QComVariant
|
||||
{
|
||||
// clang-format off
|
||||
QComVariant() noexcept
|
||||
{
|
||||
VariantInit(&m_variant);
|
||||
}
|
||||
|
||||
~QComVariant() noexcept
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
explicit QComVariant(bool value) noexcept
|
||||
{
|
||||
m_variant.vt = VT_BOOL;
|
||||
m_variant.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
|
||||
}
|
||||
|
||||
explicit QComVariant(int value) noexcept
|
||||
{
|
||||
m_variant.vt = VT_INT;
|
||||
m_variant.intVal = value;
|
||||
}
|
||||
|
||||
explicit QComVariant(long value) noexcept
|
||||
{
|
||||
m_variant.vt = VT_I4;
|
||||
m_variant.lVal = value;
|
||||
}
|
||||
|
||||
explicit QComVariant(double value) noexcept
|
||||
{
|
||||
m_variant.vt = VT_R8;
|
||||
m_variant.dblVal = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QComVariant(const ComPtr<T> &value) noexcept
|
||||
{
|
||||
static_assert(std::is_base_of_v<IUnknown, T>, "Invalid COM interface");
|
||||
ComPtr<IUnknown> unknown = value;
|
||||
m_variant.vt = VT_UNKNOWN;
|
||||
m_variant.punkVal = unknown.Detach(); // Transfer ownership
|
||||
}
|
||||
|
||||
QComVariant(QBStr &&value) noexcept
|
||||
{
|
||||
m_variant.vt = VT_BSTR;
|
||||
m_variant.bstrVal = value.release(); // Transfer ownership
|
||||
}
|
||||
|
||||
QComVariant(const QString &str)
|
||||
{
|
||||
m_variant.vt = VT_BSTR;
|
||||
m_variant.bstrVal = QBStr{ str }.release(); // Transfer ownership of copy
|
||||
}
|
||||
|
||||
const VARIANT &get() const noexcept
|
||||
{
|
||||
return m_variant;
|
||||
}
|
||||
|
||||
VARIANT &get() noexcept
|
||||
{
|
||||
return m_variant;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
[[nodiscard]] VARIANT *operator&() noexcept // NOLINT(google-runtime-operator)
|
||||
{
|
||||
clear();
|
||||
return &m_variant;
|
||||
}
|
||||
|
||||
VARIANT release() noexcept
|
||||
{
|
||||
const VARIANT detached{ m_variant };
|
||||
VariantInit(&m_variant);
|
||||
return detached;
|
||||
}
|
||||
|
||||
QComVariant(const QComVariant &) = delete;
|
||||
QComVariant &operator=(const QComVariant &) = delete;
|
||||
QComVariant(QComVariant && other) = delete;
|
||||
QComVariant &operator=(QComVariant &&) = delete;
|
||||
|
||||
private:
|
||||
|
||||
void clear()
|
||||
{
|
||||
const HRESULT hr = VariantClear(&m_variant);
|
||||
Q_ASSERT(hr == S_OK);
|
||||
Q_UNUSED(hr)
|
||||
|
||||
VariantInit(&m_variant); // Clear value field
|
||||
}
|
||||
|
||||
VARIANT m_variant{};
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
#endif // QCOMVARIANT_P_H
|
@ -28,6 +28,7 @@
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <qpa/qplatforminputcontextfactory_p.h>
|
||||
#include <QtCore/private/qcomvariant_p.h>
|
||||
|
||||
#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
|
||||
#include <comdef.h>
|
||||
@ -85,14 +86,14 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve
|
||||
// Notifies states changes in checkboxes.
|
||||
if (accessible->role() == QAccessible::CheckBox) {
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
VARIANT oldVal, newVal;
|
||||
clearVariant(&oldVal);
|
||||
int toggleState = ToggleState_Off;
|
||||
long toggleState = ToggleState_Off;
|
||||
if (accessible->state().checked)
|
||||
toggleState = accessible->state().checkStateMixed ? ToggleState_Indeterminate : ToggleState_On;
|
||||
setVariantI4(toggleState, &newVal);
|
||||
|
||||
QComVariant oldVal;
|
||||
QComVariant newVal{toggleState};
|
||||
UiaRaiseAutomationPropertyChangedEvent(
|
||||
provider.Get(), UIA_ToggleToggleStatePropertyId, oldVal, newVal);
|
||||
provider.Get(), UIA_ToggleToggleStatePropertyId, oldVal.get(), newVal.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,24 +140,18 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve
|
||||
if (event->value().typeId() == QMetaType::QString) {
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
// Notifies changes in string values.
|
||||
VARIANT oldVal, newVal;
|
||||
clearVariant(&oldVal);
|
||||
setVariantString(event->value().toString(), &newVal);
|
||||
const QComVariant oldVal;
|
||||
const QComVariant newVal{ event->value().toString() };
|
||||
UiaRaiseAutomationPropertyChangedEvent(provider.Get(), UIA_ValueValuePropertyId,
|
||||
oldVal, newVal);
|
||||
|
||||
HRESULT hr = VariantClear(&newVal); // Free string allocated by setVariantString
|
||||
Q_ASSERT(hr == S_OK);
|
||||
Q_UNUSED(hr)
|
||||
oldVal.get(), newVal.get());
|
||||
}
|
||||
} else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
// Notifies changes in values of controls supporting the value interface.
|
||||
VARIANT oldVal, newVal;
|
||||
clearVariant(&oldVal);
|
||||
setVariantDouble(valueInterface->currentValue().toDouble(), &newVal);
|
||||
const QComVariant oldVal;
|
||||
const QComVariant newVal{ valueInterface->currentValue().toDouble() };
|
||||
UiaRaiseAutomationPropertyChangedEvent(
|
||||
provider.Get(), UIA_RangeValueValuePropertyId, oldVal, newVal);
|
||||
provider.Get(), UIA_RangeValueValuePropertyId, oldVal.get(), newVal.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -169,12 +164,10 @@ void QWindowsUiaMainProvider::notifyNameChange(QAccessibleEvent *event)
|
||||
// in order to avoid slowdowns with unnecessary notifications.
|
||||
if (accessible->role() == QAccessible::ComboBox) {
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
VARIANT oldVal, newVal;
|
||||
clearVariant(&oldVal);
|
||||
setVariantString(accessible->text(QAccessible::Name), &newVal);
|
||||
UiaRaiseAutomationPropertyChangedEvent(provider.Get(), UIA_NamePropertyId, oldVal,
|
||||
newVal);
|
||||
::SysFreeString(newVal.bstrVal);
|
||||
QComVariant oldVal;
|
||||
QComVariant newVal{ accessible->text(QAccessible::Name) };
|
||||
UiaRaiseAutomationPropertyChangedEvent(provider.Get(), UIA_NamePropertyId,
|
||||
oldVal.get(), newVal.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -424,7 +417,7 @@ void QWindowsUiaMainProvider::setAriaProperties(QAccessibleInterface *accessible
|
||||
}
|
||||
}
|
||||
|
||||
setVariantString(ariaString, pRetVal);
|
||||
*pRetVal = QComVariant{ ariaString }.release();
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::setStyle(QAccessibleInterface *accessible, VARIANT *pRetVal)
|
||||
@ -449,8 +442,8 @@ void QWindowsUiaMainProvider::setStyle(QAccessibleInterface *accessible, VARIANT
|
||||
if (level < 1 || level > 9)
|
||||
return;
|
||||
|
||||
const int styleId = styleIdForHeadingLevel(level);
|
||||
setVariantI4(styleId, pRetVal);
|
||||
const long styleId = styleIdForHeadingLevel(level);
|
||||
*pRetVal = QComVariant{ styleId }.release();
|
||||
}
|
||||
|
||||
int QWindowsUiaMainProvider::styleIdForHeadingLevel(int headingLevel)
|
||||
@ -490,24 +483,24 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
|
||||
switch (idProp) {
|
||||
case UIA_ProcessIdPropertyId:
|
||||
// PID
|
||||
setVariantI4(int(GetCurrentProcessId()), pRetVal);
|
||||
*pRetVal = QComVariant{ static_cast<long>(GetCurrentProcessId()) }.release();
|
||||
break;
|
||||
case UIA_AccessKeyPropertyId:
|
||||
// Accelerator key.
|
||||
setVariantString(accessible->text(QAccessible::Accelerator), pRetVal);
|
||||
*pRetVal = QComVariant{ accessible->text(QAccessible::Accelerator) }.release();
|
||||
break;
|
||||
case UIA_AriaPropertiesPropertyId:
|
||||
setAriaProperties(accessible, pRetVal);
|
||||
break;
|
||||
case UIA_AutomationIdPropertyId:
|
||||
// Automation ID, which can be used by tools to select a specific control in the UI.
|
||||
setVariantString(QAccessibleBridgeUtils::accessibleId(accessible), pRetVal);
|
||||
*pRetVal = QComVariant{ QAccessibleBridgeUtils::accessibleId(accessible) }.release();
|
||||
break;
|
||||
case UIA_ClassNamePropertyId:
|
||||
// Class name.
|
||||
if (QObject *o = accessible->object()) {
|
||||
QString className = QLatin1StringView(o->metaObject()->className());
|
||||
setVariantString(className, pRetVal);
|
||||
*pRetVal = QComVariant{ className }.release();
|
||||
}
|
||||
break;
|
||||
case UIA_DescribedByPropertyId:
|
||||
@ -520,12 +513,12 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
|
||||
fillVariantArrayForRelation(accessible, QAccessible::FlowsFrom, pRetVal);
|
||||
break;
|
||||
case UIA_FrameworkIdPropertyId:
|
||||
setVariantString(QStringLiteral("Qt"), pRetVal);
|
||||
*pRetVal = QComVariant{ QStringLiteral("Qt") }.release();
|
||||
break;
|
||||
case UIA_ControlTypePropertyId:
|
||||
if (topLevelWindow) {
|
||||
// Reports a top-level widget as a window, instead of "custom".
|
||||
setVariantI4(UIA_WindowControlTypeId, pRetVal);
|
||||
*pRetVal = QComVariant{ UIA_WindowControlTypeId }.release();
|
||||
} else {
|
||||
// Control type converted from role.
|
||||
auto controlType = roleToControlTypeId(accessible->role());
|
||||
@ -540,63 +533,66 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
|
||||
if (controlType == UIA_EditControlTypeId && (!imModuleEmpty || nativeVKDisabled))
|
||||
controlType = UIA_TextControlTypeId;
|
||||
|
||||
setVariantI4(controlType, pRetVal);
|
||||
*pRetVal = QComVariant{ controlType }.release();
|
||||
}
|
||||
break;
|
||||
case UIA_HelpTextPropertyId:
|
||||
setVariantString(accessible->text(QAccessible::Help), pRetVal);
|
||||
*pRetVal = QComVariant{ accessible->text(QAccessible::Help) }.release();
|
||||
break;
|
||||
case UIA_HasKeyboardFocusPropertyId:
|
||||
if (topLevelWindow) {
|
||||
// Windows set the active state to true when they are focused
|
||||
setVariantBool(accessible->state().active, pRetVal);
|
||||
*pRetVal = QComVariant{ accessible->state().active ? true : false }.release();
|
||||
} else {
|
||||
setVariantBool(accessible->state().focused, pRetVal);
|
||||
*pRetVal = QComVariant{ accessible->state().focused ? true : false }.release();
|
||||
}
|
||||
break;
|
||||
case UIA_IsKeyboardFocusablePropertyId:
|
||||
if (topLevelWindow) {
|
||||
// Windows should always be focusable
|
||||
setVariantBool(true, pRetVal);
|
||||
*pRetVal = QComVariant{ true }.release();
|
||||
} else {
|
||||
setVariantBool(accessible->state().focusable, pRetVal);
|
||||
*pRetVal = QComVariant{ accessible->state().focusable ? true : false }.release();
|
||||
}
|
||||
break;
|
||||
case UIA_IsOffscreenPropertyId:
|
||||
setVariantBool(accessible->state().offscreen, pRetVal);
|
||||
*pRetVal = QComVariant{ accessible->state().offscreen ? true : false }.release();
|
||||
break;
|
||||
case UIA_IsContentElementPropertyId:
|
||||
setVariantBool(true, pRetVal);
|
||||
*pRetVal = QComVariant{ true }.release();
|
||||
break;
|
||||
case UIA_IsControlElementPropertyId:
|
||||
setVariantBool(true, pRetVal);
|
||||
*pRetVal = QComVariant{ true }.release();
|
||||
break;
|
||||
case UIA_IsEnabledPropertyId:
|
||||
setVariantBool(!accessible->state().disabled, pRetVal);
|
||||
*pRetVal = QComVariant{ !accessible->state().disabled }.release();
|
||||
break;
|
||||
case UIA_IsPasswordPropertyId:
|
||||
setVariantBool(accessible->role() == QAccessible::EditableText
|
||||
&& accessible->state().passwordEdit, pRetVal);
|
||||
*pRetVal = QComVariant{ accessible->role() == QAccessible::EditableText
|
||||
&& accessible->state().passwordEdit }
|
||||
.release();
|
||||
break;
|
||||
case UIA_IsPeripheralPropertyId:
|
||||
// True for peripheral UIs.
|
||||
if (QWindow *window = windowForAccessible(accessible)) {
|
||||
const Qt::WindowType wt = window->type();
|
||||
setVariantBool(wt == Qt::Popup || wt == Qt::ToolTip || wt == Qt::SplashScreen, pRetVal);
|
||||
*pRetVal = QComVariant{ wt == Qt::Popup || wt == Qt::ToolTip || wt == Qt::SplashScreen }
|
||||
.release();
|
||||
}
|
||||
break;
|
||||
case UIA_IsDialogPropertyId:
|
||||
setVariantBool(accessible->role() == QAccessible::Dialog
|
||||
|| accessible->role() == QAccessible::AlertMessage, pRetVal);
|
||||
*pRetVal = QComVariant{ accessible->role() == QAccessible::Dialog
|
||||
|| accessible->role() == QAccessible::AlertMessage }
|
||||
.release();
|
||||
break;
|
||||
case UIA_FullDescriptionPropertyId:
|
||||
setVariantString(accessible->text(QAccessible::Description), pRetVal);
|
||||
*pRetVal = QComVariant{ accessible->text(QAccessible::Description) }.release();
|
||||
break;
|
||||
case UIA_NamePropertyId: {
|
||||
QString name = accessible->text(QAccessible::Name);
|
||||
if (name.isEmpty() && topLevelWindow)
|
||||
name = QCoreApplication::applicationName();
|
||||
setVariantString(name, pRetVal);
|
||||
*pRetVal = QComVariant{ name }.release();
|
||||
break;
|
||||
}
|
||||
case UIA_StyleIdAttributeId:
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qvarlengtharray.h>
|
||||
#include <QtCore/private/qcomvariant_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@ -157,15 +158,15 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextRangeProvider::GetAttributeValue(TEXTAT
|
||||
|
||||
switch (attributeId) {
|
||||
case UIA_IsReadOnlyAttributeId:
|
||||
setVariantBool(accessible->state().readOnly, pRetVal);
|
||||
*pRetVal = QComVariant{ accessible->state().readOnly ? true : false }.release();
|
||||
break;
|
||||
case UIA_CaretPositionAttributeId:
|
||||
if (textInterface->cursorPosition() == 0)
|
||||
setVariantI4(CaretPosition_BeginningOfLine, pRetVal);
|
||||
*pRetVal = QComVariant{ static_cast<long>(CaretPosition_BeginningOfLine) }.release();
|
||||
else if (textInterface->cursorPosition() == textInterface->characterCount())
|
||||
setVariantI4(CaretPosition_EndOfLine, pRetVal);
|
||||
*pRetVal = QComVariant{ static_cast<long>(CaretPosition_EndOfLine) }.release();
|
||||
else
|
||||
setVariantI4(CaretPosition_Unknown, pRetVal);
|
||||
*pRetVal = QComVariant{ static_cast<long>(CaretPosition_Unknown) }.release();
|
||||
break;
|
||||
case UIA_StrikethroughStyleAttributeId:
|
||||
{
|
||||
@ -173,7 +174,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextRangeProvider::GetAttributeValue(TEXTAT
|
||||
if (value.isEmpty())
|
||||
break;
|
||||
const TextDecorationLineStyle uiaLineStyle = uiaLineStyleForIA2LineStyle(value);
|
||||
setVariantI4(uiaLineStyle, pRetVal);
|
||||
*pRetVal = QComVariant{ static_cast<long>(uiaLineStyle) }.release();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -63,30 +63,6 @@ void clearVariant(VARIANT *variant)
|
||||
variant->punkVal = nullptr;
|
||||
}
|
||||
|
||||
void setVariantI4(int value, VARIANT *variant)
|
||||
{
|
||||
variant->vt = VT_I4;
|
||||
variant->lVal = value;
|
||||
}
|
||||
|
||||
void setVariantBool(bool value, VARIANT *variant)
|
||||
{
|
||||
variant->vt = VT_BOOL;
|
||||
variant->boolVal = value ? -1 : 0;
|
||||
}
|
||||
|
||||
void setVariantDouble(double value, VARIANT *variant)
|
||||
{
|
||||
variant->vt = VT_R8;
|
||||
variant->dblVal = value;
|
||||
}
|
||||
|
||||
void setVariantString(const QString &value, VARIANT *variant)
|
||||
{
|
||||
variant->vt = VT_BSTR;
|
||||
variant->bstrVal = QBStr(value).release();
|
||||
}
|
||||
|
||||
// Scales a rect to native coordinates, according to high dpi settings.
|
||||
void rectToNativeUiaRect(const QRect &rect, const QWindow *w, UiaRect *uiaRect)
|
||||
{
|
||||
|
@ -33,14 +33,6 @@ bool isTextUnitSeparator(TextUnit unit, const QChar &ch);
|
||||
|
||||
void clearVariant(VARIANT *variant);
|
||||
|
||||
void setVariantI4(int value, VARIANT *variant);
|
||||
|
||||
void setVariantBool(bool value, VARIANT *variant);
|
||||
|
||||
void setVariantDouble(double value, VARIANT *variant);
|
||||
|
||||
void setVariantString(const QString &value, VARIANT *variant);
|
||||
|
||||
} // namespace QWindowsUiAutomation
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -3,3 +3,4 @@
|
||||
|
||||
add_subdirectory(qcomobject)
|
||||
add_subdirectory(qbstr)
|
||||
add_subdirectory(qcomvariant)
|
||||
|
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2024 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
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_qcomvariant
|
||||
SOURCES
|
||||
tst_qcomvariant.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
)
|
@ -0,0 +1,199 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <private/qcomvariant_p.h>
|
||||
#include <private/qcomobject_p.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
struct IUnknownStub : QComObject<IUnknown>
|
||||
{
|
||||
ULONG refCount()
|
||||
{
|
||||
AddRef();
|
||||
return Release();
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
void __cdecl SetOaNoCache(void);
|
||||
}
|
||||
|
||||
class tst_qcomvariant : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
void initTestCase()
|
||||
{
|
||||
SetOaNoCache();
|
||||
}
|
||||
|
||||
private slots:
|
||||
|
||||
static void constructor_initializesToEmpty()
|
||||
{
|
||||
const QComVariant var;
|
||||
QCOMPARE_EQ(var.get().vt, VT_EMPTY);
|
||||
}
|
||||
|
||||
static void constructor_initializes_withBool()
|
||||
{
|
||||
QComVariant falseVal{ false };
|
||||
QCOMPARE_EQ(falseVal.get().vt, VT_BOOL);
|
||||
QCOMPARE_EQ(falseVal.get().boolVal, VARIANT_FALSE);
|
||||
|
||||
QComVariant trueVal{ true };
|
||||
QCOMPARE_EQ(trueVal.get().vt, VT_BOOL);
|
||||
QCOMPARE_EQ(trueVal.get().boolVal, VARIANT_TRUE);
|
||||
}
|
||||
|
||||
static void constructor_initializes_withInt()
|
||||
{
|
||||
QComVariant var{ 3 };
|
||||
QCOMPARE_EQ(var.get().vt, VT_INT);
|
||||
QCOMPARE_EQ(var.get().intVal, 3);
|
||||
}
|
||||
|
||||
static void constructor_initializes_withLong()
|
||||
{
|
||||
QComVariant var{ 4l };
|
||||
QCOMPARE_EQ(var.get().vt, VT_I4);
|
||||
QCOMPARE_EQ(var.get().lVal, 4);
|
||||
}
|
||||
|
||||
static void constructor_initializes_withDouble()
|
||||
{
|
||||
QComVariant var{ 3.14 };
|
||||
QCOMPARE_EQ(var.get().vt, VT_R8);
|
||||
QVERIFY(qFuzzyCompare(var.get().dblVal, 3.14));
|
||||
}
|
||||
|
||||
static void constructor_initializes_withQBStr()
|
||||
{
|
||||
QComVariant var{ QBStr{ L"Hello world!" } };
|
||||
QCOMPARE_EQ(var.get().vt, VT_BSTR);
|
||||
QCOMPARE_EQ(var.get().bstrVal, QStringView(L"Hello world!"));
|
||||
}
|
||||
|
||||
static void constructor_initializesWithNullQBStr()
|
||||
{
|
||||
QComVariant var{ QBStr{ nullptr } };
|
||||
QCOMPARE_EQ(var.get().vt, VT_BSTR);
|
||||
QCOMPARE_EQ(var.get().bstrVal, nullptr);
|
||||
}
|
||||
|
||||
static void constructor_initializes_withQString()
|
||||
{
|
||||
QComVariant var{ "Hello world!"_L1 };
|
||||
QCOMPARE_EQ(var.get().vt, VT_BSTR);
|
||||
QCOMPARE_EQ(var.get().bstrVal, QStringView(L"Hello world!"));
|
||||
}
|
||||
|
||||
static void constructor_initializesWithNullQString()
|
||||
{
|
||||
QComVariant var{ QString{} };
|
||||
QCOMPARE_EQ(var.get().vt, VT_BSTR);
|
||||
QCOMPARE_EQ(var.get().bstrVal, nullptr);
|
||||
}
|
||||
|
||||
static void constructor_initializesWithEmptyQString()
|
||||
{
|
||||
QComVariant var{ QString{ ""_L1 } };
|
||||
QCOMPARE_EQ(var.get().vt, VT_BSTR);
|
||||
QCOMPARE_EQ(var.get().bstrVal, ""_L1);
|
||||
}
|
||||
|
||||
static void constructor_initializes_withIUnknown()
|
||||
{
|
||||
const ComPtr<IUnknownStub> value = makeComObject<IUnknownStub>();
|
||||
|
||||
QComVariant var{ value };
|
||||
|
||||
QCOMPARE_EQ(var.get().vt, VT_UNKNOWN);
|
||||
QCOMPARE_EQ(var.get().punkVal, value.Get());
|
||||
}
|
||||
|
||||
static void constructor_initializes_withNullptrIUnknown()
|
||||
{
|
||||
ComPtr<IUnknown> ptr;
|
||||
QComVariant var{ ptr };
|
||||
|
||||
QCOMPARE_EQ(var.get().vt, VT_UNKNOWN);
|
||||
QCOMPARE_EQ(var.get().punkVal, nullptr);
|
||||
}
|
||||
|
||||
static void addressOfOperator_clearsVariant_whenVariantOwnsIUnknown()
|
||||
{
|
||||
// Arrange
|
||||
ComPtr<IUnknownStub> value = makeComObject<IUnknownStub>();
|
||||
|
||||
QComVariant var{ value };
|
||||
QCOMPARE_EQ(value->refCount(), 2);
|
||||
|
||||
// Act
|
||||
const VARIANT *ptr = &var;
|
||||
|
||||
// Assert
|
||||
QCOMPARE_EQ(value->refCount(), 1);
|
||||
QCOMPARE_EQ(ptr->vt, VT_EMPTY);
|
||||
QCOMPARE_EQ(ptr->punkVal, nullptr);
|
||||
}
|
||||
|
||||
static void destructor_clearsVariant_whenOwningIUnknown()
|
||||
{
|
||||
// Arrange
|
||||
const ComPtr<IUnknownStub> value = makeComObject<IUnknownStub>();
|
||||
|
||||
{
|
||||
QComVariant var{ value };
|
||||
QCOMPARE_EQ(value->refCount(), 2);
|
||||
// Act (run destructor)
|
||||
}
|
||||
|
||||
// Assert
|
||||
QCOMPARE_EQ(value->refCount(), 1);
|
||||
}
|
||||
|
||||
static void destructor_doesNotCrash_whenOwningBSTR()
|
||||
{
|
||||
// Arrange
|
||||
QComVariant var{ QBStr{ L"Hello world!" } };
|
||||
|
||||
// Act (run destructor)
|
||||
}
|
||||
|
||||
static void release_releasesOwnershipAndReturnsVariant_whenOwningIUnknown()
|
||||
{
|
||||
// Arrange
|
||||
const ComPtr<IUnknownStub> value = makeComObject<IUnknownStub>();
|
||||
|
||||
VARIANT detached{};
|
||||
{
|
||||
QComVariant var{ value };
|
||||
QCOMPARE_EQ(value->refCount(), 2);
|
||||
|
||||
// Act (Release and run destructor)
|
||||
detached = var.release();
|
||||
}
|
||||
|
||||
// Assert
|
||||
QCOMPARE_EQ(value->refCount(), 2);
|
||||
QCOMPARE_EQ(detached.vt, VT_UNKNOWN);
|
||||
QCOMPARE_EQ(detached.punkVal, value.Get());
|
||||
|
||||
// Cleanup
|
||||
QCOMPARE_EQ(VariantClear(&detached), S_OK);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_qcomvariant)
|
||||
#include "tst_qcomvariant.moc"
|
Loading…
x
Reference in New Issue
Block a user