qtactiveqt/tests/auto/conversion/tst_conversion.cpp

466 lines
14 KiB
C++

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "comutil_p.h"
#include "testutil_p.h"
#include <qaxtypes_p.h>
#include <ActiveQt/qaxobject.h>
#include <QtTest/QtTest>
#include <QtCore/QVariant>
#include <QtCore/QDateTime>
#include <QtCore/QMetaType>
#include <qt_windows.h>
#include <wrl/client.h>
using Microsoft::WRL::ComPtr;
QT_BEGIN_NAMESPACE
class tst_Conversion : public QObject
{
Q_OBJECT
private slots:
void conversion_data();
void conversion();
void VARIANTToQVariant_ReturnsBool_WhenCalledWithVariantBool();
void QVariantToVARIANT_ReturnsVariantBool_WhenCalledWithBool();
void VARIANTToQVariant_ReturnsString_WhenCalledWithString_data();
void VARIANTToQVariant_ReturnsString_WhenCalledWithString();
void QVariantToVARIANT_ReturnsString_WhenCalledWithString_data();
void QVariantToVARIANT_ReturnsString_WhenCalledWithString();
void VARIANTToQVariant_ReturnsCopyOfValue_WhenCalledWithMaxPossibleValueOrPointer_data();
void VARIANTToQVariant_ReturnsCopyOfValue_WhenCalledWithMaxPossibleValueOrPointer();
void QVariantToVARIANT_ReturnsCopyOfValue_WhenCalledWithMaxPossibleValue_data();
void QVariantToVARIANT_ReturnsCopyOfValue_WhenCalledWithMaxPossibleValue();
void VARIANTToQVariant_DoesNotIncreaseRefCount_WhenGivenAnIUnknown();
void QVariantToVARIANT_RecoversIUnknown_WhenQVariantHasIUnknown();
void VARIANTToQVariant_DoesNotIncreaseRefCount_WhenGivenAnIDispatch();
void QVariantToVARIANT_RecoversIDispatch_WhenQVariantHasIDispatch();
void VARIANTToQVariant_IncreasesRefCount_WhenCalledWithQVariantTypeName();
void ObserveThat_VARIANTToQVariant_ReturnsEmptyQVariant_WhenWrappingIDispatchInQAxObjectPtr();
void VARIANTToQVariant_CreatesQAxObject_WhenCalledWithMetaTypeId();
private:
template<typename T>
static void addScalarMaxValueRow();
};
enum Mode {
ByValue,
ByReference, // Allocate new value
OutParameter // Pre-allocated out-parameter by reference (test works only for types < qint64)
};
Q_DECLARE_METATYPE(Mode)
void tst_Conversion::conversion_data()
{
QTest::addColumn<QVariant>("value");
QTest::addColumn<uint>("expectedType");
QTest::addColumn<QByteArray>("typeName");
QTest::addColumn<Mode>("mode");
QVariant qvar;
QByteArray typeName;
qvar = QVariant('a');
typeName = QByteArrayLiteral("char");
QTest::newRow("char")
<< qvar << uint(VT_I1) << typeName << ByValue;
QTest::newRow("char-ref")
<< qvar << uint(VT_I1 | VT_BYREF) << typeName << ByReference;
QTest::newRow("char-out")
<< qvar << uint(VT_I1 | VT_BYREF) << typeName << OutParameter;
qvar = QVariant(uchar(197));
typeName = QByteArrayLiteral("uchar");
QTest::newRow("uchar")
<< qvar << uint(VT_UI1) << typeName << ByValue;
QTest::newRow("uchar-ref")
<< qvar << uint(VT_UI1 | VT_BYREF) << typeName << ByReference;
QTest::newRow("uchar-out")
<< qvar << uint(VT_UI1 | VT_BYREF) << typeName << OutParameter;
qvar = QVariant(ushort(42));
typeName = QByteArrayLiteral("ushort");
QTest::newRow("ushort")
<< qvar << uint(VT_UI2) << typeName << ByValue;
QTest::newRow("ushort-ref")
<< qvar << uint(VT_UI2 | VT_BYREF) << typeName << ByReference;
QTest::newRow("ushort-out")
<< qvar << uint(VT_UI2 | VT_BYREF) << typeName << OutParameter;
qvar = QVariant(short(42));
typeName = QByteArrayLiteral("short");
QTest::newRow("short")
<< qvar << uint(VT_I2) << typeName << ByValue;
QTest::newRow("short-ref")
<< qvar << uint(VT_I2 | VT_BYREF) << typeName << ByReference;
QTest::newRow("short-out")
<< qvar << uint(VT_I2 | VT_BYREF) << typeName << OutParameter;
qvar = QVariant(42);
typeName.clear();
QTest::newRow("int")
<< qvar << uint(VT_I4) << typeName << ByValue;
QTest::newRow("int-ref")
<< qvar << uint(VT_I4 | VT_BYREF) << typeName << ByReference;
QTest::newRow("int-out")
<< qvar << uint(VT_I4 | VT_BYREF) << typeName << OutParameter;
qvar = QVariant(42u);
typeName.clear();
QTest::newRow("uint")
<< qvar << uint(VT_UI4) << typeName << ByValue;
QTest::newRow("uint-ref")
<< qvar << uint(VT_UI4 | VT_BYREF) << typeName << ByReference;
QTest::newRow("uint-out")
<< qvar << uint(VT_UI4 | VT_BYREF) << typeName << OutParameter;
qvar = QVariant(qint64(42));
typeName.clear();
QTest::newRow("int64")
<< qvar << uint(VT_I8) << typeName << ByValue;
QTest::newRow("int64-ref")
<< qvar << uint(VT_I8 | VT_BYREF) << typeName << ByReference;
QTest::newRow("int64-out")
<< qvar << uint(VT_I8 | VT_BYREF) << typeName << OutParameter;
qvar = QVariant(quint64(42u));
typeName.clear();
QTest::newRow("uint64")
<< qvar << uint(VT_UI8) << typeName << ByValue;
QTest::newRow("uint64-ref")
<< qvar << uint(VT_UI8 | VT_BYREF) << typeName << ByReference;
QTest::newRow("uint64-out")
<< qvar << uint(VT_UI8 | VT_BYREF) << typeName << OutParameter;
qvar = QVariant(3.141f);
typeName = QByteArrayLiteral("float");
QTest::newRow("float")
<< qvar << uint(VT_R4) << typeName << ByValue;
QTest::newRow("float-ref")
<< qvar << uint(VT_R4 | VT_BYREF) << typeName << ByReference;
QTest::newRow("float-out")
<< qvar << uint(VT_R4 | VT_BYREF) << typeName << OutParameter;
qvar = QVariant(3.141);
typeName.clear();
QTest::newRow("double")
<< qvar << uint(VT_R8) << typeName << ByValue;
QTest::newRow("double-ref")
<< qvar << uint(VT_R8 | VT_BYREF) << typeName << ByReference;
QTest::newRow("double-out")
<< qvar << uint(VT_R8 | VT_BYREF) << typeName << OutParameter;
qvar = QDateTime(QDate(1968, 3, 9), QTime(10, 0));
typeName.clear();
QTest::newRow("datetime")
<< qvar << uint(VT_DATE) << typeName << ByValue;
QTest::newRow("datetime-ref")
<< qvar << uint(VT_DATE | VT_BYREF) << typeName << ByReference;
QTest::newRow("datetime-out")
<< qvar << uint(VT_DATE | VT_BYREF) << typeName << OutParameter;
}
void tst_Conversion::conversion()
{
QFETCH(QVariant, value);
QFETCH(uint, expectedType);
QFETCH(QByteArray, typeName);
QFETCH(Mode, mode);
VARIANT variant;
VariantInit(&variant);
if (mode == OutParameter) {
variant.vt = expectedType | VT_BYREF;
variant.pullVal = new ULONGLONG(0);
}
QVERIFY(QVariantToVARIANT_container(value, variant, typeName, mode != ByValue));
QCOMPARE(uint(variant.vt), expectedType);
const QVariant converted = VARIANTToQVariant_container(variant, QByteArray());
QCOMPARE(converted, value);
if (mode == OutParameter)
delete variant.pullVal;
}
void tst_Conversion::VARIANTToQVariant_ReturnsBool_WhenCalledWithVariantBool()
{
{
const ComVariant v = true;
const QVariant result = VARIANTToQVariant(v, "canBeAnything");
QVERIFY(result == true);
}
{
const ComVariant v = false;
const QVariant result = VARIANTToQVariant(v, "canBeAnything");
QVERIFY(result == false);
}
}
void tst_Conversion::QVariantToVARIANT_ReturnsVariantBool_WhenCalledWithBool()
{
{
const QVariant v = true;
ComVariant result;
QVERIFY(QVariantToVARIANT(v, result));
QVERIFY(result.vt == VT_BOOL);
QVERIFY(result.boolVal == VARIANT_TRUE);
}
{
const QVariant v = false;
ComVariant result;
QVERIFY(QVariantToVARIANT(v, result));
QVERIFY(result.vt == VT_BOOL);
QVERIFY(result.boolVal == VARIANT_FALSE);
}
}
void tst_Conversion::VARIANTToQVariant_ReturnsString_WhenCalledWithString_data()
{
QTest::addColumn<QString>("text");
QTest::newRow("empty") << QString{ "" };
QTest::newRow("nonempty") << QString{ "Some Latin 1 text" };
}
void tst_Conversion::VARIANTToQVariant_ReturnsString_WhenCalledWithString()
{
QFETCH(QString, text);
const ComVariant comVariant = text.toStdWString().c_str();
const QVariant actual = VARIANTToQVariant(comVariant, {});
QCOMPARE(actual, text);
}
void tst_Conversion::QVariantToVARIANT_ReturnsString_WhenCalledWithString_data()
{
QTest::addColumn<QString>("text");
QTest::newRow("empty") << QString{ "" };
QTest::newRow("nonempty") << QString{ "Some Latin 1 text" };
}
void tst_Conversion::QVariantToVARIANT_ReturnsString_WhenCalledWithString()
{
QFETCH(QString, text);
ComVariant comVariant;
QVERIFY(QVariantToVARIANT(text, comVariant));
const QString actual = QString::fromWCharArray(comVariant.bstrVal);
QCOMPARE(actual, text);
}
void tst_Conversion::
VARIANTToQVariant_ReturnsCopyOfValue_WhenCalledWithMaxPossibleValueOrPointer_data()
{
QTest::addColumn<ComVariant>("comVariant");
QTest::addColumn<QVariant>("qvariant");
addScalarMaxValueRow<char>();
addScalarMaxValueRow<unsigned char>();
addScalarMaxValueRow<short>();
addScalarMaxValueRow<unsigned short>();
addScalarMaxValueRow<int>();
addScalarMaxValueRow<unsigned int>();
addScalarMaxValueRow<long long>();
addScalarMaxValueRow<unsigned long long>();
addScalarMaxValueRow<float>();
addScalarMaxValueRow<double>();
addScalarMaxValueRow<char *>();
addScalarMaxValueRow<unsigned char *>();
addScalarMaxValueRow<short *>();
addScalarMaxValueRow<unsigned short *>();
addScalarMaxValueRow<int *>();
addScalarMaxValueRow<unsigned int *>();
addScalarMaxValueRow<long long *>();
addScalarMaxValueRow<unsigned long long *>();
addScalarMaxValueRow<float *>();
addScalarMaxValueRow<double *>();
}
void tst_Conversion::VARIANTToQVariant_ReturnsCopyOfValue_WhenCalledWithMaxPossibleValueOrPointer()
{
QFETCH(ComVariant, comVariant);
QFETCH(QVariant, qvariant);
const QVariant actual = VARIANTToQVariant(comVariant, qvariant.typeName());
QCOMPARE(actual, qvariant);
}
void tst_Conversion::QVariantToVARIANT_ReturnsCopyOfValue_WhenCalledWithMaxPossibleValue_data()
{
QTest::addColumn<ComVariant>("comVariant");
QTest::addColumn<QVariant>("qvariant");
addScalarMaxValueRow<int>();
addScalarMaxValueRow<unsigned int>();
addScalarMaxValueRow<long long>();
addScalarMaxValueRow<unsigned long long>();
addScalarMaxValueRow<float>();
addScalarMaxValueRow<double>();
}
void tst_Conversion::QVariantToVARIANT_ReturnsCopyOfValue_WhenCalledWithMaxPossibleValue()
{
QFETCH(ComVariant, comVariant);
QFETCH(QVariant, qvariant);
ComVariant actual;
QVERIFY(QVariantToVARIANT(qvariant, actual));
QCOMPARE(actual, comVariant);
}
void tst_Conversion::VARIANTToQVariant_DoesNotIncreaseRefCount_WhenGivenAnIUnknown()
{
const auto stub = makeComObject<IUnknownStub>();
const ComVariant value = stub.Get();
QVERIFY(stub->m_refCount == 2u);
const QVariant qVariant = VARIANTToQVariant(value, {});
QVERIFY(stub->m_refCount == 2u);
Q_UNUSED(qVariant);
}
void tst_Conversion::QVariantToVARIANT_RecoversIUnknown_WhenQVariantHasIUnknown()
{
const auto stub = makeComObject<IUnknownStub>();
const ComVariant value = stub.Get();
const QVariant qvar = VARIANTToQVariant(value, {});
ComVariant comVariant;
QVERIFY(QVariantToVARIANT(qvar, comVariant));
QCOMPARE(stub->m_refCount, 3u);
const ComPtr<IUnknown> recovered = comVariant.punkVal;
QCOMPARE(recovered, stub);
}
void tst_Conversion::VARIANTToQVariant_DoesNotIncreaseRefCount_WhenGivenAnIDispatch()
{
const auto stub = makeComObject<IDispatchStub>();
const ComVariant value = stub.Get();
QCOMPARE(stub->m_refCount, 2u);
const QVariant qVariant = VARIANTToQVariant(value, "IDispatch*");
QCOMPARE(stub->m_refCount, 2u);
Q_UNUSED(qVariant);
}
struct IDispatchFixture
{
const ComPtr<IDispatchStub> m_iDispatchStub = makeComObject<IDispatchStub>();
const ComVariant m_comVariant = m_iDispatchStub.Get();
const QVariant m_qVariant = VARIANTToQVariant(m_comVariant, "IDispatch*");
};
void tst_Conversion::QVariantToVARIANT_RecoversIDispatch_WhenQVariantHasIDispatch()
{
const IDispatchFixture testFixture;
QCOMPARE(testFixture.m_iDispatchStub->m_refCount, 2u);
ComVariant comVariant;
QVERIFY(QVariantToVARIANT(testFixture.m_qVariant, comVariant));
QCOMPARE(testFixture.m_iDispatchStub->m_refCount, 3u);
const ComPtr<IUnknown> recovered = comVariant.pdispVal;
QCOMPARE(recovered, testFixture.m_iDispatchStub);
}
void tst_Conversion::VARIANTToQVariant_IncreasesRefCount_WhenCalledWithQVariantTypeName()
{
const IDispatchFixture testFixture;
QCOMPARE(testFixture.m_iDispatchStub->m_refCount, 2u);
QVariant qVariant = VARIANTToQVariant(testFixture.m_comVariant, "QVariant");
qVariant = {};
// Observe that IDispatch interface is leaked here, since
// the QVariant destructor does not decrement the refcount
QCOMPARE(testFixture.m_iDispatchStub->m_refCount, 3u);
// Workaround to ensure cleanup
testFixture.m_iDispatchStub->Release();
}
void tst_Conversion::ObserveThat_VARIANTToQVariant_ReturnsEmptyQVariant_WhenWrappingIDispatchInQAxObjectPtr()
{
const IDispatchFixture testFixture;
QCOMPARE(testFixture.m_iDispatchStub->m_refCount, 2u);
qRegisterMetaType<QAxObject *>("QAxObject*");
qRegisterMetaType<QAxObject>("QAxObject");
const QVariant qVariant = VARIANTToQVariant(testFixture.m_comVariant, "QAxObject*");
QVERIFY(qVariant.isNull());
}
void tst_Conversion::VARIANTToQVariant_CreatesQAxObject_WhenCalledWithMetaTypeId()
{
const IDispatchFixture testFixture;
QCOMPARE(testFixture.m_iDispatchStub->m_refCount, 2u);
qRegisterMetaType<QAxObject *>("QAxObject*");
qRegisterMetaType<QAxObject>("QAxObject");
const QVariant qVariant = VARIANTToQVariant(testFixture.m_comVariant, "QAxObject*", QMetaType::fromType<QAxObject*>().id());
QAxObject *recovered = qVariant.value<QAxObject *>();
QVERIFY(recovered != nullptr);
}
template<typename T>
void tst_Conversion::addScalarMaxValueRow()
{
using ValueType = std::remove_pointer_t<T>;
static ValueType v = std::numeric_limits<ValueType>::max();
ComVariant comVariant;
if constexpr (std::is_pointer_v<T>)
comVariant = &v;
else
comVariant = v;
const char *typeName = QMetaType::fromType<T>().name();
QTest::newRow(typeName) << comVariant << QVariant{ v };
}
QTEST_MAIN(tst_Conversion)
QT_END_NAMESPACE
#include "tst_conversion.moc"