diff options
Diffstat (limited to 'tests/auto')
20 files changed, 780 insertions, 129 deletions
diff --git a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp index 32344f1e26..1b7d410beb 100644 --- a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp +++ b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp @@ -49,6 +49,7 @@ private slots: void debugWithBool() const; void debugSpaceHandling() const; void debugNoQuotes() const; + void verbosity() const; void stateSaver() const; void veryLongWarningMessage() const; void qDebugQChar() const; @@ -192,7 +193,11 @@ public: QDebug operator<< (QDebug s, const MyLine& line) { const QDebugStateSaver saver(s); - s.nospace() << "MyLine(" << line.p1 << ", " << line.p2 << ")"; + s.nospace(); + s << "MyLine(" << line.p1 << ", "<< line.p2; + if (s.verbosity() > 2) + s << ", Manhattan length=" << (qAbs(line.p2.v1 - line.p1.v1) + qAbs(line.p2.v2 - line.p1.v2)); + s << ')'; return s; } @@ -255,6 +260,33 @@ void tst_QDebug::debugNoQuotes() const QCOMPARE(s_msg, QString::fromLatin1("'H' \"Hello\" \"Hello\" H Hello Hello")); } +void tst_QDebug::verbosity() const +{ + MyLine line(MyPoint(10, 11), MyPoint (12, 13)); + QString output; + QDebug d(&output); + d.nospace(); + d << line << '\n'; + const int oldVerbosity = d.verbosity(); + d.setVerbosity(0); + QCOMPARE(d.verbosity(), 0); + d.setVerbosity(7); + QCOMPARE(d.verbosity(), 7); + const int newVerbosity = oldVerbosity + 2; + d.setVerbosity(newVerbosity); + QCOMPARE(d.verbosity(), newVerbosity); + d << line << '\n'; + d.setVerbosity(oldVerbosity ); + QCOMPARE(d.verbosity(), oldVerbosity ); + d << line; + const QStringList lines = output.split(QLatin1Char('\n')); + QCOMPARE(lines.size(), 3); + // Verbose should be longer + QVERIFY2(lines.at(1).size() > lines.at(0).size(), qPrintable(lines.join(QLatin1Char(',')))); + // Switching back to brief produces same output + QCOMPARE(lines.at(0).size(), lines.at(2).size()); +} + void tst_QDebug::stateSaver() const { MessageHandlerSetter mhs(myMessageHandler); diff --git a/tests/auto/corelib/io/qfileselector/platforms/+haiku/test b/tests/auto/corelib/io/qfileselector/platforms/+haiku/test new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/corelib/io/qfileselector/platforms/+haiku/test diff --git a/tests/auto/corelib/io/qfileselector/platforms/+haiku/test2 b/tests/auto/corelib/io/qfileselector/platforms/+haiku/test2 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/corelib/io/qfileselector/platforms/+haiku/test2 diff --git a/tests/auto/corelib/io/qfileselector/platforms/+unix/+haiku/test b/tests/auto/corelib/io/qfileselector/platforms/+unix/+haiku/test new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/corelib/io/qfileselector/platforms/+unix/+haiku/test diff --git a/tests/auto/corelib/io/qfileselector/qfileselector.qrc b/tests/auto/corelib/io/qfileselector/qfileselector.qrc index 661647f933..6e2699774d 100644 --- a/tests/auto/corelib/io/qfileselector/qfileselector.qrc +++ b/tests/auto/corelib/io/qfileselector/qfileselector.qrc @@ -19,6 +19,7 @@ <file>platforms/+unix/+darwin/+mac/+osx/test</file> <file>platforms/+unix/+darwin/+mac/test</file> <file>platforms/+unix/+darwin/test</file> + <file>platforms/+unix/+haiku/test</file> <file>platforms/+unix/+linux/test</file> <file>platforms/+unix/test</file> <file>platforms/+windows/+wince/test</file> @@ -30,6 +31,7 @@ <file>platforms/+osx/test</file> <file>platforms/+darwin/test</file> <file>platforms/+mac/test</file> + <file>platforms/+haiku/test</file> <file>platforms/+linux/test</file> <file>platforms/+wince/test</file> @@ -39,6 +41,7 @@ <file>platforms/+blackberry/test2</file> <file>platforms/+ios/test2</file> <file>platforms/+osx/test2</file> + <file>platforms/+haiku/test2</file> <file>platforms/+linux/test2</file> <file>platforms/+wince/test2</file> <file>platforms/+winnt/test2</file> diff --git a/tests/auto/corelib/io/qfileselector/tst_qfileselector.cpp b/tests/auto/corelib/io/qfileselector/tst_qfileselector.cpp index b3767b4887..87381f4c4e 100644 --- a/tests/auto/corelib/io/qfileselector/tst_qfileselector.cpp +++ b/tests/auto/corelib/io/qfileselector/tst_qfileselector.cpp @@ -89,7 +89,7 @@ void tst_QFileSelector::basicTest_data() QString expectedPlatform2File(""); //Only the last selector QString expectedPlatform3File; // Only the first selector (the family) #if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_BLACKBERRY) && \ - !defined(Q_OS_DARWIN) && !defined(Q_OS_LINUX) + !defined(Q_OS_DARWIN) && !defined(Q_OS_LINUX) && !defined(Q_OS_HAIKU) /* We are only aware of specific unixes, and do not have test files for any of the others. However those unixes can get a selector added from the result of a uname call, so this will lead to a case where we don't have that file so we can't expect the concatenation of platform diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index a192ccde59..6da8f55e61 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -3357,9 +3357,6 @@ void tst_QVariant::numericalConvert_data() void tst_QVariant::numericalConvert() { -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(__x86_64__) - QSKIP("Known to fail due to a GCC bug on at least Ubuntu 10.04 32-bit - check QTBUG-8959"); -#endif QFETCH(QVariant, v); QFETCH(bool, isInteger); double num = isInteger ? 5 : 5.3; diff --git a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp index bdbb291d7f..cd77b188cc 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +++ b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp @@ -120,6 +120,7 @@ private slots: void loadGarbage(); #endif void relativePath(); + void absolutePath(); void reloadPlugin(); void preloadedPlugin_data(); void preloadedPlugin(); @@ -406,6 +407,20 @@ void tst_QPluginLoader::relativePath() QVERIFY(loader.unload()); } +void tst_QPluginLoader::absolutePath() +{ + // Windows binaries run from release and debug subdirs, so we can't rely on the current dir. + const QString binDir = QFINDTESTDATA("bin"); + QVERIFY(!binDir.isEmpty()); + QVERIFY(QDir::isAbsolutePath(binDir)); + QPluginLoader loader(binDir + "/theplugin"); + loader.load(); // not recommended, instance() should do the job. + PluginInterface *instance = qobject_cast<PluginInterface*>(loader.instance()); + QVERIFY(instance); + QCOMPARE(instance->pluginName(), QLatin1String("Plugin ok")); + QVERIFY(loader.unload()); +} + void tst_QPluginLoader::reloadPlugin() { QPluginLoader loader; diff --git a/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp index 7b1b7ce963..6e09ebb09b 100644 --- a/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp +++ b/tests/auto/corelib/tools/qcommandlineparser/testhelper/qcommandlineparser_test_helper.cpp @@ -73,6 +73,12 @@ int main(int argc, char *argv[]) "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz")); parser.addOption(newlineOption); + // A hidden option + QCommandLineOption hiddenOption(QStringList() << QStringLiteral("hidden")); + hiddenOption.setDescription(QStringLiteral("THIS SHOULD NEVER APPEAR")); + hiddenOption.setHidden(true); + parser.addOption(hiddenOption); + // This program supports different options depending on the "command" (first argument). // Call parse() to find out the positional arguments. parser.parse(QCoreApplication::arguments()); diff --git a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp index 6ff46ed20b..9c4ded69de 100644 --- a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp +++ b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp @@ -35,6 +35,7 @@ #include <QtCore/QCommandLineParser> Q_DECLARE_METATYPE(char**) +Q_DECLARE_METATYPE(QCommandLineParser::OptionsAfterPositionalArgumentsMode) class tst_QCommandLineParser : public QObject { @@ -51,6 +52,8 @@ private slots: void testPositionalArguments(); void testBooleanOption_data(); void testBooleanOption(); + void testOptionsAndPositional_data(); + void testOptionsAndPositional(); void testMultipleNames_data(); void testMultipleNames(); void testSingleValueOption_data(); @@ -141,6 +144,40 @@ void tst_QCommandLineParser::testBooleanOption() QVERIFY(!parser.isSet("c")); } +void tst_QCommandLineParser::testOptionsAndPositional_data() +{ + QTest::addColumn<QStringList>("args"); + QTest::addColumn<QStringList>("expectedOptionNames"); + QTest::addColumn<bool>("expectedIsSet"); + QTest::addColumn<QStringList>("expectedPositionalArguments"); + QTest::addColumn<QCommandLineParser::OptionsAfterPositionalArgumentsMode>("parsingMode"); + + const QStringList arg = QStringList() << "arg"; + QTest::newRow("before_positional_default") << (QStringList() << "tst_qcommandlineparser" << "-b" << "arg") << (QStringList() << "b") << true << arg << QCommandLineParser::ParseAsOptions; + QTest::newRow("after_positional_default") << (QStringList() << "tst_qcommandlineparser" << "arg" << "-b") << (QStringList() << "b") << true << arg << QCommandLineParser::ParseAsOptions; + QTest::newRow("before_positional_parseAsArg") << (QStringList() << "tst_qcommandlineparser" << "-b" << "arg") << (QStringList() << "b") << true << arg << QCommandLineParser::ParseAsPositionalArguments; + QTest::newRow("after_positional_parseAsArg") << (QStringList() << "tst_qcommandlineparser" << "arg" << "-b") << (QStringList()) << false << (QStringList() << "arg" << "-b") << QCommandLineParser::ParseAsPositionalArguments; +} + +void tst_QCommandLineParser::testOptionsAndPositional() +{ + QFETCH(QStringList, args); + QFETCH(QStringList, expectedOptionNames); + QFETCH(bool, expectedIsSet); + QFETCH(QStringList, expectedPositionalArguments); + QFETCH(QCommandLineParser::OptionsAfterPositionalArgumentsMode, parsingMode); + + QCoreApplication app(empty_argc, empty_argv); + QCommandLineParser parser; + parser.setOptionsAfterPositionalArgumentsMode(parsingMode); + QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("b"), QStringLiteral("a boolean option")))); + QVERIFY(parser.parse(args)); + QCOMPARE(parser.optionNames(), expectedOptionNames); + QCOMPARE(parser.isSet("b"), expectedIsSet); + QCOMPARE(parser.values("b"), QStringList()); + QCOMPARE(parser.positionalArguments(), expectedPositionalArguments); +} + void tst_QCommandLineParser::testMultipleNames_data() { QTest::addColumn<QStringList>("args"); diff --git a/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp b/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp index 8c29064457..33e143fa7d 100644 --- a/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp +++ b/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp @@ -60,7 +60,7 @@ void tst_QRingBuffer::sizeWhenReserved() QRingBuffer ringBuffer; ringBuffer.reserve(5); - QCOMPARE(ringBuffer.size(), 5); + QCOMPARE(ringBuffer.size(), Q_INT64_C(5)); } void tst_QRingBuffer::sizeWhenReservedAndChopped() @@ -69,14 +69,14 @@ void tst_QRingBuffer::sizeWhenReservedAndChopped() ringBuffer.reserve(31337); ringBuffer.chop(31337); - QCOMPARE(ringBuffer.size(), 0); + QCOMPARE(ringBuffer.size(), Q_INT64_C(0)); } void tst_QRingBuffer::sizeWhenEmpty() { QRingBuffer ringBuffer; - QCOMPARE(ringBuffer.size(), 0); + QCOMPARE(ringBuffer.size(), Q_INT64_C(0)); } void tst_QRingBuffer::readPointerAtPositionReadTooMuch() @@ -101,22 +101,22 @@ void tst_QRingBuffer::readPointerAtPositionWithHead() qint64 length; const char* buf2 = ringBuffer.readPointerAtPosition(0, length); - QCOMPARE(length, qint64(2)); + QCOMPARE(length, Q_INT64_C(2)); QVERIFY(*buf2 == '2'); QVERIFY(*(buf2+1) == '3'); // advance 2 more, ringBuffer should be empty then ringBuffer.free(2); buf2 = ringBuffer.readPointerAtPosition(0, length); - QCOMPARE(length, qint64(0)); + QCOMPARE(length, Q_INT64_C(0)); QVERIFY(buf2 == 0); // check buffer with 2 blocks memcpy(ringBuffer.reserve(4), "0123", 4); ringBuffer.append(QByteArray("45678", 5)); ringBuffer.free(3); - buf2 = ringBuffer.readPointerAtPosition(1, length); - QCOMPARE(length, qint64(5)); + buf2 = ringBuffer.readPointerAtPosition(Q_INT64_C(1), length); + QCOMPARE(length, Q_INT64_C(5)); } void tst_QRingBuffer::readPointerAtPositionEmptyRead() @@ -149,14 +149,14 @@ void tst_QRingBuffer::readPointerAtPositionWriteRead() // write in chunks of 50 bytes // this ensures there will be multiple QByteArrays inside the QRingBuffer // since QRingBuffer is then only using individual arrays of around 4000 bytes - qint64 thisWrite = qMin(remaining, qint64(50)); + qint64 thisWrite = qMin(remaining, Q_INT64_C(50)); char *pos = ringBuffer.reserve(thisWrite); inData.read(pos, thisWrite); remaining -= thisWrite; } // was data put into it? QVERIFY(ringBuffer.size() > 0); - QCOMPARE(qint64(ringBuffer.size()), inData.size()); + QCOMPARE(ringBuffer.size(), inData.size()); //read from the QRingBuffer in loop, put back into another QBuffer QBuffer outData; @@ -187,12 +187,12 @@ void tst_QRingBuffer::free() ringBuffer.append(QByteArray("01234", 5)); ringBuffer.free(1); - QCOMPARE(ringBuffer.size(), 4095 + 2048 + 5); + QCOMPARE(ringBuffer.size(), Q_INT64_C(4095 + 2048 + 5)); ringBuffer.free(4096); - QCOMPARE(ringBuffer.size(), 2047 + 5); + QCOMPARE(ringBuffer.size(), Q_INT64_C(2047 + 5)); ringBuffer.free(48); ringBuffer.free(2000); - QCOMPARE(ringBuffer.size(), 4); + QCOMPARE(ringBuffer.size(), Q_INT64_C(4)); QVERIFY(memcmp(ringBuffer.readPointer(), "1234", 4) == 0); } @@ -211,8 +211,8 @@ void tst_QRingBuffer::reserveAndRead() for (int i = 1; i < 256; ++i) { QByteArray ba; ba.resize(i); - int thisRead = ringBuffer.read(ba.data(), i); - QCOMPARE(thisRead, i); + qint64 thisRead = ringBuffer.read(ba.data(), i); + QCOMPARE(thisRead, qint64(i)); QVERIFY(ba.count(char(i)) == i); } QVERIFY(ringBuffer.size() == 0); @@ -227,12 +227,12 @@ void tst_QRingBuffer::chop() ringBuffer.reserve(4096); ringBuffer.chop(1); - QCOMPARE(ringBuffer.size(), 5 + 2048 + 4095); + QCOMPARE(ringBuffer.size(), Q_INT64_C(5 + 2048 + 4095)); ringBuffer.chop(4096); - QCOMPARE(ringBuffer.size(), 5 + 2047); + QCOMPARE(ringBuffer.size(), Q_INT64_C(5 + 2047)); ringBuffer.chop(48); ringBuffer.chop(2000); - QCOMPARE(ringBuffer.size(), 4); + QCOMPARE(ringBuffer.size(), Q_INT64_C(4)); QVERIFY(memcmp(ringBuffer.readPointer(), "0123", 4) == 0); } @@ -248,7 +248,7 @@ void tst_QRingBuffer::ungetChar() ringBuffer.getChar(); ringBuffer.ungetChar(char(c)); // unget first char } - QCOMPARE(ringBuffer.size(), 1); + QCOMPARE(ringBuffer.size(), Q_INT64_C(1)); } void tst_QRingBuffer::indexOf() @@ -258,8 +258,8 @@ void tst_QRingBuffer::indexOf() ringBuffer.putChar(char(i)); for (int i = 1; i < 256; ++i) { - int index = ringBuffer.indexOf(char(i)); - QCOMPARE(i - 1, index); + qint64 index = ringBuffer.indexOf(char(i)); + QCOMPARE(qint64(i - 1), index); QCOMPARE(index, ringBuffer.indexOf(char(i), i)); QVERIFY(ringBuffer.indexOf(char(i), i - 1) == -1); // test for absent char } @@ -298,7 +298,7 @@ void tst_QRingBuffer::readLine() // check first empty string reading stringBuf[0] = char(0xFF); - QCOMPARE(ringBuffer.readLine(stringBuf, int(sizeof(stringBuf)) - 2), ba2.size()); + QCOMPARE(ringBuffer.readLine(stringBuf, int(sizeof(stringBuf)) - 2), qint64(ba2.size())); QVERIFY(stringBuf[0] == ba2[0]); QVERIFY(ringBuffer.readLine(stringBuf, int(sizeof(stringBuf)) - 2) == (ba3.size() + ba4.size() diff --git a/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp b/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp index 32c228c186..d85e842a23 100644 --- a/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp +++ b/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp @@ -105,6 +105,8 @@ private slots: void sendWithGui(); void sendAsync(); void sendSignal(); + void sendSignalToName(); + void sendSignalToOtherName(); void registerObject_data(); void registerObject(); @@ -189,6 +191,49 @@ void tst_QDBusConnection::sendSignal() QTest::qWait(1000); } +void tst_QDBusConnection::sendSignalToName() +{ + QDBusSpy spy; + + QDBusConnection con = QDBusConnection::sessionBus(); + + con.connect(con.baseService(), "/org/kde/selftest", "org.kde.selftest", "ping", &spy, + SLOT(handlePing(QString))); + + QDBusMessage msg = + QDBusMessage::createTargetedSignal(con.baseService(), "/org/kde/selftest", + "org.kde.selftest", "ping"); + msg << QLatin1String("ping"); + + QVERIFY(con.send(msg)); + + QTest::qWait(1000); + + QCOMPARE(spy.args.count(), 1); + QCOMPARE(spy.args.at(0).toString(), QString("ping")); +} + +void tst_QDBusConnection::sendSignalToOtherName() +{ + QDBusSpy spy; + + QDBusConnection con = QDBusConnection::sessionBus(); + + con.connect(con.baseService(), "/org/kde/selftest", "org.kde.selftest", "ping", &spy, + SLOT(handlePing(QString))); + + QDBusMessage msg = + QDBusMessage::createTargetedSignal("some.other.service", "/org/kde/selftest", + "org.kde.selftest", "ping"); + msg << QLatin1String("ping"); + + QVERIFY(con.send(msg)); + + QTest::qWait(1000); + + QCOMPARE(spy.args.count(), 0); +} + void tst_QDBusConnection::send() { QDBusConnection con = QDBusConnection::sessionBus(); diff --git a/tests/auto/gui/painting/qcolor/tst_qcolor.cpp b/tests/auto/gui/painting/qcolor/tst_qcolor.cpp index 4fafbf9827..7d17794c0b 100644 --- a/tests/auto/gui/painting/qcolor/tst_qcolor.cpp +++ b/tests/auto/gui/painting/qcolor/tst_qcolor.cpp @@ -38,6 +38,7 @@ #include <qcolor.h> #include <qdebug.h> +#include <qrgba64.h> #include <private/qdrawingprimitive_sse2_p.h> class tst_QColor : public QObject @@ -105,6 +106,9 @@ private slots: void premultiply(); void unpremultiply_sse4(); + void qrgba64(); + void qrgba64Premultiply(); + void qrgba64Equivalence(); #ifdef Q_DEAD_CODE_FROM_QT4_X11 void setallowX11ColorNames(); @@ -1455,7 +1459,7 @@ void tst_QColor::unpremultiply_sse4() for (uint a = 0; a < 256; a++) { for (uint c = 0; c <= a; c++) { QRgb p = qRgba(c, a-c, c, a); - QCOMPARE(qUnpremultiply(p), qUnpremultiply_sse4(p)); + QCOMPARE(qUnpremultiply_sse4(p), qUnpremultiply(p)); } } return; @@ -1464,5 +1468,84 @@ void tst_QColor::unpremultiply_sse4() QSKIP("SSE4 not supported on this CPU."); } +void tst_QColor::qrgba64() +{ + QRgba64 rgb64 = QRgba64::fromRgba(0x22, 0x33, 0x44, 0xff); + QCOMPARE(rgb64.red(), quint16(0x2222)); + QCOMPARE(rgb64.green(), quint16(0x3333)); + QCOMPARE(rgb64.blue(), quint16(0x4444)); + QCOMPARE(rgb64.alpha(), quint16(0xffff)); + + QColor c(rgb64); + QCOMPARE(c.red(), 0x22); + QCOMPARE(c.green(), 0x33); + QCOMPARE(c.blue(), 0x44); + + QCOMPARE(c.rgba64(), rgb64); + + QColor c2 = QColor::fromRgb(0x22, 0x33, 0x44, 0xff); + QCOMPARE(c, c2); + QCOMPARE(c2.rgba64(), rgb64); + + rgb64.setAlpha(0x8000); + rgb64.setGreen(0x8844); + rgb64 = rgb64.premultiplied(); + QCOMPARE(rgb64.red(), quint16(0x1111)); + QCOMPARE(rgb64.blue(), quint16(0x2222)); + QCOMPARE(rgb64.green(), quint16(0x4422)); +} + +void tst_QColor::qrgba64Premultiply() +{ + // Tests that qPremultiply(qUnpremultiply(rgba64)) returns rgba64. + for (uint a = 0; a < 0x10000; a+=7) { + const uint step = std::max(a/1024, 1u); + for (uint c = 0; c <= a; c+=step) { + QRgba64 p = qRgba64(c, a-c, a-c/2, a); + QRgba64 pp = qPremultiply(qUnpremultiply(p)); + QCOMPARE(pp, p); + } + } +} + +void tst_QColor::qrgba64Equivalence() +{ + // Any ARGB32 converted back and forth. + for (uint a = 0; a < 256; a++) { + for (uint c = 0; c < 256; c++) { + QRgb p1 = qRgba(c, 255-c, 255-c, a); + QRgba64 p64 = QRgba64::fromArgb32(p1); + QCOMPARE(p64.toArgb32(), p1); + } + } + // Any unpremultiplied ARGB32 value premultipled in RGB64 (except alpha 0). + for (uint a = 1; a < 256; a++) { + for (uint c = 0; c < 256; c++) { + QRgb p1 = qRgba(c, 255-c, 255-c, a); + QRgb pp1 = qPremultiply(p1); + QRgba64 pp64 = qPremultiply(QRgba64::fromArgb32(p1)); + QRgb pp2 = pp64.toArgb32(); + // 64bit premultiplied is more accurate than 32bit, so allow slight difference. + QCOMPARE(qAlpha(pp2), qAlpha(pp1)); + QVERIFY(qAbs(qRed(pp2)-qRed(pp1)) <= 1); + QVERIFY(qAbs(qGreen(pp2)-qGreen(pp1)) <= 1); + QVERIFY(qAbs(qBlue(pp2)-qBlue(pp1)) <= 1); + // But verify the added accuracy means we can return to accurate unpremultiplied ARGB32. + QRgba64 pu64 = qUnpremultiply(pp64); + QRgb p2 = pu64.toArgb32(); + QCOMPARE(p2, p1); + } + } + // Any premultiplied ARGB32 value unpremultipled in RGB64. + for (uint a = 0; a < 256; a++) { + for (uint c = 0; c <= a; c++) { + QRgb pp = qRgba(c, a-c, a-c, a); + QRgb pu = qUnpremultiply(pp); + QRgba64 pu64 = qUnpremultiply(QRgba64::fromArgb32(pp)); + QCOMPARE(pu64.toArgb32(), pu); + } + } +} + QTEST_MAIN(tst_QColor) #include "tst_qcolor.moc" diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index c63cfaf6b6..dcb3df834d 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -121,6 +121,14 @@ class tst_QNetworkReply: public QObject return s; }; + static QString tempRedirectReplyStr() { + QString s = "HTTP/1.1 307 Temporary Redirect\r\n" + "Content-Type: text/plain\r\n" + "location: %1\r\n" + "\r\n"; + return s; + }; + QEventLoop *loop; enum RunSimpleRequestReturn { Timeout = 0, Success, Failure }; int returnCode; @@ -456,6 +464,11 @@ private Q_SLOTS: void putWithRateLimiting(); + void ioHttpSingleRedirect(); + void ioHttpChangeMaxRedirects(); + void ioHttpRedirectErrors_data(); + void ioHttpRedirectErrors(); + // NOTE: This test must be last! void parentingRepliesToTheApp(); private: @@ -593,10 +606,16 @@ protected: virtual void reply() { Q_ASSERT(!client.isNull()); // we need to emulate the bytesWrittenSlot call if the data is empty. - if (dataToTransmit.size() == 0) + if (dataToTransmit.size() == 0) { QMetaObject::invokeMethod(this, "bytesWrittenSlot", Qt::QueuedConnection); - else + } else { client->write(dataToTransmit); + // FIXME: For SSL connections, if we don't flush the socket, the + // client never receives the data and since we're doing a disconnect + // immediately afterwards, it causes a RemoteHostClosedError for the + // client + client->flush(); + } } private: void connectSocketSignals() @@ -7962,7 +7981,142 @@ void tst_QNetworkReply::putWithRateLimiting() QCOMPARE(uploadedData, data); } +void tst_QNetworkReply::ioHttpSingleRedirect() +{ + QUrl localhost = QUrl("http://localhost"); + QByteArray http200Reply = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; + + // Setup server to which the second server will redirect to + MiniHttpServer server2(http200Reply); + + QUrl redirectUrl = QUrl(localhost); + redirectUrl.setPort(server2.serverPort()); + + QByteArray tempRedirectReply = + tempRedirectReplyStr().arg(QString(redirectUrl.toEncoded())).toLatin1(); + + + // Setup redirect server + MiniHttpServer server(tempRedirectReply); + + localhost.setPort(server.serverPort()); + QNetworkRequest request(localhost); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + + QNetworkReplyPtr reply(manager.get(request)); + QSignalSpy redSpy(reply.data(), SIGNAL(redirected(QUrl))); + QSignalSpy finSpy(reply.data(), SIGNAL(finished())); + + QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); + + // Redirected and finished should be emitted exactly once + QCOMPARE(redSpy.count(), 1); + QCOMPARE(finSpy.count(), 1); + + // Original URL should not be changed after redirect + QCOMPARE(request.url(), localhost); + + // Verify Redirect url + QList<QVariant> args = redSpy.takeFirst(); + QCOMPARE(args.at(0).toUrl(), redirectUrl); + + // Reply url is set to the redirect url + QCOMPARE(reply->url(), redirectUrl); + QCOMPARE(reply->error(), QNetworkReply::NoError); +} + +void tst_QNetworkReply::ioHttpChangeMaxRedirects() +{ + QUrl localhost = QUrl("http://localhost"); + QByteArray http200Reply = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; + + MiniHttpServer server1(""); + MiniHttpServer server2(""); + MiniHttpServer server3(http200Reply); + + QUrl server2Url(localhost); + server2Url.setPort(server2.serverPort()); + server1.setDataToTransmit(tempRedirectReplyStr().arg( + QString(server2Url.toEncoded())).toLatin1()); + + QUrl server3Url(localhost); + server3Url.setPort(server3.serverPort()); + server2.setDataToTransmit(tempRedirectReplyStr().arg( + QString(server3Url.toEncoded())).toLatin1()); + + localhost.setPort(server1.serverPort()); + QNetworkRequest request(localhost); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + + // Set Max redirects to 1. This will cause TooManyRedirectsError + request.setMaximumRedirectsAllowed(1); + + QNetworkReplyPtr reply(manager.get(request)); + QSignalSpy redSpy(reply.data(), SIGNAL(redirected(QUrl))); + QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QVERIFY(waitForFinish(reply) == Failure); + + QCOMPARE(redSpy.count(), request.maximumRedirectsAllowed()); + QCOMPARE(spy.count(), 1); + QVERIFY(reply->error() == QNetworkReply::TooManyRedirectsError); + + // Increase max redirects to allow successful completion + request.setMaximumRedirectsAllowed(3); + + QNetworkReplyPtr reply2(manager.get(request)); + QSignalSpy redSpy2(reply2.data(), SIGNAL(redirected(QUrl))); + + QVERIFY2(waitForFinish(reply2) == Success, msgWaitForFinished(reply2)); + + QCOMPARE(redSpy2.count(), 2); + QCOMPARE(reply2->url(), server3Url); + QVERIFY(reply2->error() == QNetworkReply::NoError); +} + +void tst_QNetworkReply::ioHttpRedirectErrors_data() +{ + QTest::addColumn<QString>("url"); + QTest::addColumn<QString>("dataToSend"); + QTest::addColumn<QNetworkReply::NetworkError>("error"); + + QString tempRedirectReply = QString("HTTP/1.1 307 Temporary Redirect\r\n" + "Content-Type: text/plain\r\n" + "location: http://localhost:%1\r\n\r\n"); + + QTest::newRow("too-many-redirects") << "http://localhost" << tempRedirectReply << QNetworkReply::TooManyRedirectsError; + QTest::newRow("insecure-redirect") << "https://localhost" << tempRedirectReply << QNetworkReply::InsecureRedirectError; + QTest::newRow("unknown-redirect") << "http://localhost"<< tempRedirectReply.replace("http", "bad_protocol") << QNetworkReply::ProtocolUnknownError; +} + +void tst_QNetworkReply::ioHttpRedirectErrors() +{ + QFETCH(QString, url); + QFETCH(QString, dataToSend); + QFETCH(QNetworkReply::NetworkError, error); + + QUrl localhost(url); + MiniHttpServer server("", localhost.scheme() == "https"); + + localhost.setPort(server.serverPort()); + + QByteArray d2s = dataToSend.arg( + QString::number(server.serverPort())).toLatin1(); + server.setDataToTransmit(d2s); + + QNetworkRequest request(localhost); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QNetworkReplyPtr reply(manager.get(request)); + if (localhost.scheme() == "https") + reply.data()->ignoreSslErrors(); + QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QVERIFY(waitForFinish(reply) == Failure); + + QCOMPARE(spy.count(), 1); + QVERIFY(reply->error() == error); +} // NOTE: This test must be last testcase in tst_qnetworkreply! void tst_QNetworkReply::parentingRepliesToTheApp() diff --git a/tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp b/tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp index 76c36831bd..1c79b6c016 100644 --- a/tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp +++ b/tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp @@ -37,11 +37,6 @@ #include <qcoreapplication.h> #include <qdebug.h> #include <qabstractsocket.h> -#include <qtcpserver.h> -#include <qtcpsocket.h> -#ifndef QT_NO_SSL -#include <qsslsocket.h> -#endif class tst_QAbstractSocket : public QObject { @@ -52,9 +47,7 @@ public: virtual ~tst_QAbstractSocket(); private slots: - void initTestCase(); void getSetCheck(); - void serverDisconnectWithBuffered(); }; tst_QAbstractSocket::tst_QAbstractSocket() @@ -73,11 +66,6 @@ public: void setPeerPort(quint16 port) { QAbstractSocket::setPeerPort(port); } }; -void tst_QAbstractSocket::initTestCase() -{ - qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState"); -} - // Testing get/set functions void tst_QAbstractSocket::getSetCheck() { @@ -106,46 +94,5 @@ void tst_QAbstractSocket::getSetCheck() QCOMPARE(quint16(0xffff), obj1.peerPort()); } -// Test buffered socket being properly closed on remote disconnect -void tst_QAbstractSocket::serverDisconnectWithBuffered() -{ - QTcpServer tcpServer; -#ifndef QT_NO_SSL - QSslSocket testSocket; -#else - QTcpSocket testSocket; -#endif - - QVERIFY(tcpServer.listen(QHostAddress::LocalHost)); - testSocket.connectToHost(tcpServer.serverAddress(), tcpServer.serverPort()); - // Accept connection on server side - QVERIFY(tcpServer.waitForNewConnection(5000)); - QTcpSocket *newConnection = tcpServer.nextPendingConnection(); - // Send one char and drop link - QVERIFY(newConnection != NULL); - QVERIFY(newConnection->putChar(0)); - QVERIFY(newConnection->flush()); - delete newConnection; - - QVERIFY(testSocket.waitForConnected(5000)); // ready for write - QVERIFY(testSocket.state() == QAbstractSocket::ConnectedState); - - QSignalSpy spyStateChanged(&testSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState))); - QSignalSpy spyDisconnected(&testSocket, SIGNAL(disconnected())); - - QVERIFY(testSocket.waitForReadyRead(5000)); // have one char already in internal buffer - char buf[128]; - QCOMPARE(testSocket.read(buf, sizeof(buf)), Q_INT64_C(1)); - if (testSocket.state() != QAbstractSocket::UnconnectedState) { - QVERIFY(testSocket.waitForDisconnected(5000)); - QVERIFY(testSocket.state() == QAbstractSocket::UnconnectedState); - } - // Test signal emitting - QVERIFY(spyDisconnected.count() == 1); - QVERIFY(spyStateChanged.count() > 0); - QVERIFY(qvariant_cast<QAbstractSocket::SocketState>(spyStateChanged.last().first()) - == QAbstractSocket::UnconnectedState); -} - QTEST_MAIN(tst_QAbstractSocket) #include "tst_qabstractsocket.moc" diff --git a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp index 0ba9b6a58c..109e48ed74 100644 --- a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp +++ b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp @@ -200,6 +200,7 @@ private slots: void setSocketOption(); void clientSendDataOnDelayedDisconnect(); + void serverDisconnectWithBuffered(); protected slots: void nonBlockingIMAP_hostFound(); @@ -2984,5 +2985,50 @@ void tst_QTcpSocket::clientSendDataOnDelayedDisconnect() delete socket; } +// Test buffered socket being properly closed on remote disconnect +void tst_QTcpSocket::serverDisconnectWithBuffered() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState"); + + QTcpServer tcpServer; + QTcpSocket *socket = newSocket(); + + QVERIFY(tcpServer.listen(QHostAddress::LocalHost)); + socket->connectToHost(tcpServer.serverAddress(), tcpServer.serverPort()); + // Accept connection on server side + QVERIFY(tcpServer.waitForNewConnection(5000)); + QTcpSocket *newConnection = tcpServer.nextPendingConnection(); + // Send one char and drop link + QVERIFY(newConnection != NULL); + QVERIFY(newConnection->putChar(0)); + QVERIFY(newConnection->flush()); + delete newConnection; + + QVERIFY(socket->waitForConnected(5000)); // ready for write + QVERIFY(socket->state() == QAbstractSocket::ConnectedState); + + QSignalSpy spyStateChanged(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState))); + QSignalSpy spyDisconnected(socket, SIGNAL(disconnected())); + + QVERIFY(socket->waitForReadyRead(5000)); // have one char already in internal buffer + char buf[128]; + QCOMPARE(socket->read(buf, sizeof(buf)), Q_INT64_C(1)); + if (socket->state() != QAbstractSocket::UnconnectedState) { + QVERIFY(socket->waitForDisconnected(5000)); + QVERIFY(socket->state() == QAbstractSocket::UnconnectedState); + } + // Test signal emitting + QVERIFY(spyDisconnected.count() == 1); + QVERIFY(spyStateChanged.count() > 0); + QVERIFY(qvariant_cast<QAbstractSocket::SocketState>(spyStateChanged.last().first()) + == QAbstractSocket::UnconnectedState); + + delete socket; +} + QTEST_MAIN(tst_QTcpSocket) #include "tst_qtcpsocket.moc" diff --git a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp index 9937b6f7f8..8061b1ccb9 100644 --- a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp +++ b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp @@ -668,7 +668,7 @@ void tst_QAccessibility::textAttributes_data() defaultComplexFont.setStyle(QFont::StyleItalic); defaultComplexFont.setUnderline(true); - static QStringList defaults = QString("font-style:normal;font-weight:normal;text-align:left;text-position:baseline;text-underline-style:none;font-size:13pt").split(';'); + static QStringList defaults = QString("font-style:normal;font-weight:normal;text-align:left;text-position:baseline;font-size:13pt").split(';'); static QStringList bold = defaults; bold[1] = QString::fromLatin1("font-weight:bold"); @@ -683,7 +683,7 @@ void tst_QAccessibility::textAttributes_data() monospace.append(QLatin1String("font-family:\"monospace\"")); static QStringList font8pt = defaults; - font8pt[5] = (QLatin1String("font-size:8pt")); + font8pt[4] = (QLatin1String("font-size:8pt")); static QStringList color = defaults; color << QLatin1String("color:rgb(240,241,242)") << QLatin1String("background-color:rgb(20,240,30)"); @@ -694,8 +694,9 @@ void tst_QAccessibility::textAttributes_data() static QStringList defaultFontDifferent = defaults; defaultFontDifferent[0] = QString::fromLatin1("font-style:italic"); defaultFontDifferent[1] = QString::fromLatin1("font-weight:bold"); - defaultFontDifferent[4] = QString::fromLatin1("text-underline-style:solid"); - defaultFontDifferent[5] = QString::fromLatin1("font-size:20pt"); + defaultFontDifferent[4] = QString::fromLatin1("font-size:20pt"); + defaultFontDifferent.append("text-underline-style:solid"); + defaultFontDifferent.append("text-underline-type:single"); defaultFontDifferent.append("font-family:\"Arial\""); static QStringList defaultFontDifferentBoldItalic = defaultFontDifferent; @@ -703,10 +704,10 @@ void tst_QAccessibility::textAttributes_data() defaultFontDifferentBoldItalic[1] = QString::fromLatin1("font-weight:bold"); static QStringList defaultFontDifferentMonospace = defaultFontDifferent; - defaultFontDifferentMonospace[6] = (QLatin1String("font-family:\"monospace\"")); + defaultFontDifferentMonospace[7] = (QLatin1String("font-family:\"monospace\"")); static QStringList defaultFontDifferentFont8pt = defaultFontDifferent; - defaultFontDifferentFont8pt[5] = (QLatin1String("font-size:8pt")); + defaultFontDifferentFont8pt[4] = (QLatin1String("font-size:8pt")); static QStringList defaultFontDifferentColor = defaultFontDifferent; defaultFontDifferentColor << QLatin1String("color:rgb(240,241,242)") << QLatin1String("background-color:rgb(20,240,30)"); @@ -1715,7 +1716,10 @@ static QRect characterRect(const QTextEdit &edit, int offset) QPointF layoutPosition = layout->position(); int relativeOffset = offset - block.position(); QTextLine line = layout->lineForTextPosition(relativeOffset); - QFontMetrics fm(edit.currentFont()); + QTextBlock::iterator it = block.begin(); + while (!it.fragment().contains(offset)) + ++it; + QFontMetrics fm(it.fragment().charFormat().font()); QChar ch = edit.document()->characterAt(offset); int w = fm.width(ch); int h = fm.height(); @@ -1747,6 +1751,7 @@ void tst_QAccessibility::textEditTest() for (int pass = 0; pass < 2; ++pass) { { QTextEdit edit; + edit.setMinimumSize(600, 400); setFrameless(&edit); int startOffset; int endOffset; @@ -1757,7 +1762,7 @@ void tst_QAccessibility::textEditTest() QFont font("Helvetica"); font.setPointSizeF(12.5); font.setWordSpacing(1.1); - edit.setCurrentFont(font); + edit.document()->setDefaultFont(font); } edit.show(); @@ -1779,7 +1784,7 @@ void tst_QAccessibility::textEditTest() QCOMPARE(startOffset, 13); QCOMPARE(endOffset, 31); QCOMPARE(textIface->characterCount(), 48); - QFontMetrics fm(edit.currentFont()); + QFontMetrics fm(edit.document()->defaultFont()); QCOMPARE(textIface->characterRect(0).size(), QSize(fm.width("h"), fm.height())); QCOMPARE(textIface->characterRect(5).size(), QSize(fm.width(" "), fm.height())); QCOMPARE(textIface->characterRect(6).size(), QSize(fm.width("w"), fm.height())); diff --git a/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac.cpp b/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac.cpp index 8e439a61cf..92af5a0757 100644 --- a/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac.cpp +++ b/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac.cpp @@ -75,6 +75,7 @@ private slots: void lineEditTest(); void hierarchyTest(); void notificationsTest(); + void checkBoxTest(); private: AccessibleTestWindow *m_window; @@ -151,5 +152,19 @@ void tst_QAccessibilityMac::notificationsTest() QVERIFY(notifications(m_window)); } +void tst_QAccessibilityMac::checkBoxTest() +{ + if (!macNativeAccessibilityEnabled()) + return; + + QCheckBox *cb = new QCheckBox(m_window); + cb->setText("Great option"); + m_window->addWidget(cb); + QVERIFY(QTest::qWaitForWindowExposed(m_window)); + QCoreApplication::processEvents(); + + QVERIFY(testCheckBox()); +} + QTEST_MAIN(tst_QAccessibilityMac) #include "tst_qaccessibilitymac.moc" diff --git a/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac_helpers.h b/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac_helpers.h index 78615fa9cd..cdf12489a6 100644 --- a/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac_helpers.h +++ b/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac_helpers.h @@ -44,3 +44,4 @@ bool testLineEdit(); bool testHierarchy(QWidget *w); bool singleWidget(); bool notifications(QWidget *w); +bool testCheckBox(); diff --git a/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac_helpers.mm b/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac_helpers.mm index b004e985bd..d0ff6af640 100644 --- a/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac_helpers.mm +++ b/tests/auto/other/qaccessibilitymac/tst_qaccessibilitymac_helpers.mm @@ -60,6 +60,43 @@ bool trusted() return AXIsProcessTrusted(); } +struct AXErrorTag { + AXError err; + explicit AXErrorTag(AXError theErr) : err(theErr) {} +}; + +QDebug operator<<(QDebug dbg, AXErrorTag err) +{ + QDebugStateSaver saver(dbg); + + const char *errDesc = 0; + const char *errName = 0; + switch (err.err) { +#define HANDLE_ERR(error, desc) case kAXError##error: errName = "kAXError" #error; errDesc = desc; break + HANDLE_ERR(Success, "Success"); + HANDLE_ERR(Failure, "A system error occurred, such as the failure to allocate an object."); + HANDLE_ERR(IllegalArgument, "An illegal argument was passed to the function."); + HANDLE_ERR(InvalidUIElement, "The AXUIElementRef passed to the function is invalid."); + HANDLE_ERR(InvalidUIElementObserver, "The AXObserverRef passed to the function is not a valid observer."); + HANDLE_ERR(CannotComplete, "The function cannot complete because messaging failed in some way or because the application with which the function is communicating is busy or unresponsive."); + HANDLE_ERR(AttributeUnsupported, "The attribute is not supported by the AXUIElementRef."); + HANDLE_ERR(ActionUnsupported, "The action is not supported by the AXUIElementRef."); + HANDLE_ERR(NotificationUnsupported, "The notification is not supported by the AXUIElementRef."); + HANDLE_ERR(NotImplemented, "Indicates that the function or method is not implemented (this can be returned if a process does not support the accessibility API)."); + HANDLE_ERR(NotificationAlreadyRegistered, "This notification has already been registered for."); + HANDLE_ERR(NotificationNotRegistered, "Indicates that a notification is not registered yet."); + HANDLE_ERR(APIDisabled, "The accessibility API is disabled (as when, for example, the user deselects \"Enable access for assistive devices\" in Universal Access Preferences)."); + HANDLE_ERR(NoValue, "The requested value or AXUIElementRef does not exist."); + HANDLE_ERR(ParameterizedAttributeUnsupported, "The parameterized attribute is not supported by the AXUIElementRef."); + HANDLE_ERR(NotEnoughPrecision, "Not enough precision."); + default: errName = "<unknown error>"; errDesc = "UNKNOWN ERROR"; break; + } +#undef HANDLE_ERR + + dbg.nospace() << "AXError(value=" << err.err << ", name=" << errName << ", description=\"" << errDesc << "\")"; + + return dbg; +} #define EXPECT(cond) \ if (!(cond)) { \ @@ -71,41 +108,21 @@ bool trusted() @interface TestAXObject : NSObject { AXUIElementRef reference; - NSString *_role; - NSString *_title; - NSString *_description; - NSString *_value; - CGRect _rect; } @property (readonly) NSString *role; @property (readonly) NSString *title; @property (readonly) NSString *description; @property (readonly) NSString *value; @property (readonly) CGRect rect; + @property (readonly) NSArray *actions; @end @implementation TestAXObject - @synthesize role = _role; - @synthesize title = _title; - @synthesize description = _description; - @synthesize value = _value; - @synthesize rect = _rect; - - (id) initWithAXUIElementRef: (AXUIElementRef) ref { if ( self = [super init] ) { reference = ref; - AXUIElementCopyAttributeValue(ref, kAXRoleAttribute, (CFTypeRef*)&_role); - AXUIElementCopyAttributeValue(ref, kAXTitleAttribute, (CFTypeRef*)&_title); - AXUIElementCopyAttributeValue(ref, kAXDescriptionAttribute, (CFTypeRef*)&_description); - AXUIElementCopyAttributeValue(ref, kAXValueAttribute, (CFTypeRef*)&_value); - AXValueRef sizeValue; - AXUIElementCopyAttributeValue(ref, kAXSizeAttribute, (CFTypeRef*)&sizeValue); - AXValueGetValue(sizeValue, kAXValueCGSizeType, &_rect.size); - AXValueRef positionValue; - AXUIElementCopyAttributeValue(ref, kAXPositionAttribute, (CFTypeRef*)&positionValue); - AXValueGetValue(positionValue, kAXValueCGPointType, &_rect.origin); } return self; } @@ -140,24 +157,18 @@ bool trusted() - (AXUIElementRef) findDirectChildByRole: (CFStringRef) role { - AXUIElementRef result = nil; + TestAXObject *result = nil; NSArray *childList = [self childList]; for (id child in childList) { - CFStringRef typeString; - AXUIElementCopyAttributeValue((AXUIElementRef)child, kAXRoleAttribute, (CFTypeRef*)&typeString); - if (CFStringCompare(typeString, role, 0) == 0) { - result = (AXUIElementRef) child; + TestAXObject *childObject = [[TestAXObject alloc] initWithAXUIElementRef:(AXUIElementRef)child]; + if ([childObject.role isEqualToString:(NSString*)role]) { + result = childObject; break; } } - return result; -} - -- (AXUIElementRef) parent -{ - AXUIElementRef p = nil; - AXUIElementCopyAttributeValue(reference, kAXParentAttribute, (CFTypeRef*)&p); - return p; + AXUIElementRef ret = [result ref]; + [result release]; + return ret; } + (TestAXObject *) getApplicationAXObject @@ -168,6 +179,208 @@ bool trusted() return appObject; } ++ (NSInteger)_numberFromValue:(CFTypeRef)value +{ + NSInteger number = -1; + if (!CFNumberGetValue((CFNumberRef)value, kCFNumberNSIntegerType, &number)) + { + qDebug() << "Could not get NSInteger value out of CFNumberRef"; + } + return number; +} + ++ (BOOL)_boolFromValue:(CFTypeRef)value +{ + return CFBooleanGetValue((CFBooleanRef)value); +} + ++ (NSRange)_rangeFromValue:(CFTypeRef)value +{ + CFRange cfRange; + NSRange range = NSMakeRange(0, 0); + + if (!AXValueGetValue((AXValueRef)value, kAXValueCFRangeType, &cfRange)) + qDebug() << "Could not get CFRange value out of AXValueRef"; + else if (cfRange.location < 0 || cfRange.length < 0) + qDebug() << "Cannot convert CFRange with negative location or length to NSRange"; + else if (static_cast<uintmax_t>(cfRange.location) > NSUIntegerMax || static_cast<uintmax_t>(cfRange.length) > NSUIntegerMax) + qDebug() << "Cannot convert CFRange with location or length out of bounds for NSUInteger"; + else + { + range.length = static_cast<NSUInteger>(cfRange.length); + range.location = static_cast<NSUInteger>(cfRange.location); + } + + return range; +} + ++ (NSRect)_rectFromValue:(CFTypeRef)value +{ + NSRect rect = NSMakeRect(0, 0, 0, 0); + if (!AXValueGetValue((AXValueRef)value, kAXValueCGRectType, reinterpret_cast<CGRect*>(&rect))) + { + qDebug() << "Could not get CGRect value out of AXValueRef"; + } + return rect; +} + ++ (NSPoint)_pointFromValue:(CFTypeRef)value +{ + NSPoint point = NSMakePoint(0, 0); + if (!AXValueGetValue((AXValueRef)value, kAXValueCGPointType, reinterpret_cast<CGPoint*>(&point))) + { + qDebug() << "Could not get CGPoint value out of AXValueRef"; + } + return point; +} + ++ (NSSize)_sizeFromValue:(CFTypeRef)value +{ + NSSize size = NSMakeSize(0, 0); + if (!AXValueGetValue((AXValueRef)value, kAXValueCGSizeType, reinterpret_cast<CGSize*>(&size))) + { + qDebug() << "Could not get CGSize value out of AXValueRef"; + } + return size; +} + +- (CFTypeRef)_attributeValue:(CFStringRef)attribute +{ + CFTypeRef value = NULL; + AXError err; + + if (kAXErrorSuccess != (err = AXUIElementCopyAttributeValue(reference, attribute, &value))) + { + qDebug() << "AXUIElementCopyAttributeValue(" << QString::fromCFString(attribute) << ") returned error = " << AXErrorTag(err); + } + return value; +} + +- (NSString*)_stringAttributeValue:(CFStringRef)attribute +{ + return (NSString*)[self _attributeValue:attribute]; +} + +- (NSInteger)_numberAttributeValue:(CFStringRef)attribute +{ + return [[self class] _numberFromValue:[self _attributeValue:attribute]]; +} + +- (BOOL)_boolAttributeValue:(CFStringRef)attribute +{ + return [[self class] _boolFromValue:[self _attributeValue:attribute]]; +} + +- (NSRange)_rangeAttributeValue:(CFStringRef)attribute +{ + return [[self class] _rangeFromValue:[self _attributeValue:attribute]]; +} + +- (NSRect)_rectAttributeValue:(CFStringRef)attribute +{ + return [[self class] _rectFromValue:[self _attributeValue:attribute]]; +} + +- (NSPoint)_pointAttributeValue:(CFStringRef)attribute +{ + return [[self class] _pointFromValue:[self _attributeValue:attribute]]; +} + +- (NSSize)_sizeAttributeValue:(CFStringRef)attribute +{ + return [[self class] _sizeFromValue:[self _attributeValue:attribute]]; +} + +- (CFTypeRef)_attributeValue:(CFStringRef)attribute forParameter:(CFTypeRef)parameter +{ + CFTypeRef value = NULL; + AXError err; + + if (kAXErrorSuccess != (err = AXUIElementCopyParameterizedAttributeValue(reference, attribute, parameter, &value))) + { + CFStringRef description = CFCopyDescription(parameter); + qDebug() << "AXUIElementCopyParameterizedAttributeValue(" << QString::fromCFString(attribute) << ", parameter=" << QString::fromCFString(description) << ") returned error = " << AXErrorTag(err); + CFRelease(description); + } + return value; +} + +- (CFTypeRef)_attributeValue:(CFStringRef)attribute forRange:(NSRange)aRange +{ + CFRange cfRange = CFRangeMake(aRange.location, aRange.length); + AXValueRef range = AXValueCreate(kAXValueCFRangeType, &cfRange); + CFTypeRef value = [self _attributeValue:attribute forParameter:range]; + CFRelease(range); + return value; +} + +- (CFTypeRef)_attributeValue:(CFStringRef)attribute forNumber:(NSInteger)aNumber +{ + CFNumberRef number = CFNumberCreate(NULL, kCFNumberNSIntegerType, &aNumber); + CFTypeRef value = [self _attributeValue:attribute forParameter:number]; + CFRelease(number); + return value; +} + +- (CFTypeRef)_attributeValue:(CFStringRef)attribute forPoint:(CGPoint)aPoint +{ + AXValueRef point = AXValueCreate(kAXValueCGPointType, &aPoint); + CFTypeRef value = [self _attributeValue:attribute forParameter:point]; + CFRelease(point); + return value; +} + +- (NSArray*)actions +{ + AXError err; + CFArrayRef actions; + + if (kAXErrorSuccess != (err = AXUIElementCopyActionNames(reference, &actions))) + { + qDebug() << "AXUIElementCopyActionNames(...) returned error = " << AXErrorTag(err); + } + + return (NSArray*)actions; +} + +- (void)performAction:(CFStringRef)action +{ + AXError err; + + if (kAXErrorSuccess != (err = AXUIElementPerformAction(reference, action))) + { + qDebug() << "AXUIElementPerformAction(" << QString::fromCFString(action) << ") returned error = " << AXErrorTag(err); + } +} + +- (NSString*) role { return [self _stringAttributeValue:kAXRoleAttribute]; } +- (NSString*) title { return [self _stringAttributeValue:kAXTitleAttribute]; } +- (NSString*) description { return [self _stringAttributeValue:kAXDescriptionAttribute]; } +- (NSString*) value { return [self _stringAttributeValue:kAXValueAttribute]; } +- (NSInteger) valueNumber { return [self _numberAttributeValue:kAXValueAttribute]; } +- (NSRect) rect +{ + NSRect rect; + rect.origin = [self _pointAttributeValue:kAXPositionAttribute]; + rect.size = [self _sizeAttributeValue:kAXSizeAttribute]; + return rect; +} +- (AXUIElementRef) parent { return (AXUIElementRef)[self _attributeValue:kAXParentAttribute]; } +- (BOOL) focused { return [self _boolAttributeValue:kAXFocusedAttribute]; } +- (NSInteger) numberOfCharacters { return [self _numberAttributeValue:kAXNumberOfCharactersAttribute]; } +- (NSString*) selectedText { return [self _stringAttributeValue:kAXSelectedTextAttribute]; } +- (NSRange) selectedTextRange { return [self _rangeAttributeValue:kAXSelectedTextRangeAttribute]; } +- (NSRange) visibleCharacterRange { return [self _rangeAttributeValue:kAXVisibleCharacterRangeAttribute]; } +- (NSString*) help { return [self _stringAttributeValue:kAXHelpAttribute]; } +- (NSInteger) insertionPointLineNumber { return [self _numberAttributeValue:kAXInsertionPointLineNumberAttribute]; } + +- (NSInteger) lineForIndex:(NSInteger)index { return [[self class] _numberFromValue:[self _attributeValue:kAXLineForIndexParameterizedAttribute forNumber:index]]; } +- (NSRange) rangeForLine:(NSInteger)line { return [[self class] _rangeFromValue:[self _attributeValue:kAXRangeForLineParameterizedAttribute forNumber:line]]; } +- (NSString*) stringForRange:(NSRange)range { return (NSString*)[self _attributeValue:kAXStringForRangeParameterizedAttribute forRange:range]; } +- (NSAttributedString*) attributedStringForRange:(NSRange)range { return (NSAttributedString*)[self _attributeValue:kAXAttributedStringForRangeParameterizedAttribute forRange:range]; } +- (NSRect) boundsForRange:(NSRange)range { return [[self class] _rectFromValue:[self _attributeValue:kAXBoundsForRangeParameterizedAttribute forRange:range]]; } +- (NSRange) styleRangeForIndex:(NSInteger)index { return [[self class] _rangeFromValue:[self _attributeValue:kAXStyleRangeForIndexParameterizedAttribute forNumber:index]]; } + @end @@ -225,16 +438,34 @@ bool testLineEdit() // height of window includes title bar EXPECT([window rect].size.height >= 400); - NSString *windowTitle; - AXUIElementCopyAttributeValue(windowRef, kAXTitleAttribute, (CFTypeRef*)&windowTitle); - EXPECT([windowTitle isEqualToString:@"Test window"]); + EXPECT([window.title isEqualToString:@"Test window"]); // children of window: AXUIElementRef lineEdit = [window findDirectChildByRole: kAXTextFieldRole]; EXPECT(lineEdit != nil); TestAXObject *le = [[TestAXObject alloc] initWithAXUIElementRef: lineEdit]; - EXPECT([[le value] isEqualToString:@"a11y test QLineEdit"]); + NSString *value = @"a11y test QLineEdit"; + EXPECT([le.value isEqualToString:value]); + EXPECT(value.length <= NSIntegerMax); + EXPECT(le.numberOfCharacters == static_cast<NSInteger>(value.length)); + const NSRange ranges[] = { + { 0, 0}, + { 0, 1}, + { 0, 5}, + { 5, 0}, + { 5, 1}, + { 0, value.length}, + { value.length, 0}, + }; + for (size_t i = 0; i < sizeof(ranges)/sizeof(ranges[0]); ++i) { + NSRange range = ranges[i]; + NSString *expectedSubstring = [value substringWithRange:range]; + NSString *actualSubstring = [le stringForRange:range]; + NSString *actualAttributedSubstring = [le attributedStringForRange:range].string; + EXPECT([actualSubstring isEqualTo:expectedSubstring]); + EXPECT([actualAttributedSubstring isEqualTo:expectedSubstring]); + } return true; } @@ -357,3 +588,37 @@ bool notifications(QWidget *w) return true; } + +bool testCheckBox() +{ + TestAXObject *appObject = [TestAXObject getApplicationAXObject]; + EXPECT(appObject); + + NSArray *windowList = [appObject windowList]; + // one window + EXPECT([windowList count] == 1); + AXUIElementRef windowRef = (AXUIElementRef) [windowList objectAtIndex: 0]; + EXPECT(windowRef != nil); + TestAXObject *window = [[TestAXObject alloc] initWithAXUIElementRef: windowRef]; + + // children of window: + AXUIElementRef checkBox = [window findDirectChildByRole: kAXCheckBoxRole]; + EXPECT(checkBox != nil); + + TestAXObject *cb = [[TestAXObject alloc] initWithAXUIElementRef: checkBox]; + + // here start actual checkbox tests + EXPECT([cb valueNumber] == 0); + EXPECT([cb.title isEqualToString:@"Great option"]); + // EXPECT(cb.description == nil); // currently returns "" instead of nil + + EXPECT([cb.actions containsObject:(NSString*)kAXPressAction]); + + [cb performAction:kAXPressAction]; + EXPECT([cb valueNumber] == 1); + + [cb performAction:kAXPressAction]; + EXPECT([cb valueNumber] == 0); + + return true; +} |