diff options
author | Liang Qi <liang.qi@theqtcompany.com> | 2015-03-31 18:47:56 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@theqtcompany.com> | 2015-04-01 09:10:26 +0200 |
commit | 0e6ee136c91432d4ceeeda64e5a5fa88231398d4 (patch) | |
tree | 6060e002af2900007895f6efa757989dd4c190c9 /src/testlib | |
parent | 418869d9158ea5cd998ba30778b0b7173b48161b (diff) | |
parent | 17294c5e4d15d5776f6e414b03671a4a9ed4993d (diff) |
Merge remote-tracking branch 'origin/5.5' into dev
Conflicts:
src/testlib/qtestblacklist.cpp
src/widgets/accessible/qaccessiblewidgets.cpp
Change-Id: If032adb9296428f62384ed835dbf41ee7a0b886c
Diffstat (limited to 'src/testlib')
-rw-r--r-- | src/testlib/qtestblacklist.cpp | 9 | ||||
-rw-r--r-- | src/testlib/qtestcase.cpp | 14 | ||||
-rw-r--r-- | src/testlib/qtestlog.cpp | 9 | ||||
-rw-r--r-- | src/testlib/qtestlog_p.h | 7 | ||||
-rw-r--r-- | src/testlib/qxctestlogger.mm | 501 | ||||
-rw-r--r-- | src/testlib/qxctestlogger_p.h | 95 | ||||
-rw-r--r-- | src/testlib/testlib.pro | 19 |
7 files changed, 652 insertions, 2 deletions
diff --git a/src/testlib/qtestblacklist.cpp b/src/testlib/qtestblacklist.cpp index bf795fcd68..52a9977bbf 100644 --- a/src/testlib/qtestblacklist.cpp +++ b/src/testlib/qtestblacklist.cpp @@ -39,6 +39,7 @@ #include <QtCore/qset.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qvariant.h> +#include <QtCore/QSysInfo> #include <set> @@ -128,6 +129,14 @@ static bool checkCondition(const QByteArray &condition) static QSet<QByteArray> matchedConditions = keywords(); QList<QByteArray> conds = condition.split(' '); + QByteArray distributionName = QSysInfo::productType().toLower().toUtf8(); + QByteArray distributionRelease = QSysInfo::productVersion().toLower().toUtf8(); + if (!distributionName.isEmpty()) { + if (matchedConditions.find(distributionName) == matchedConditions.end()) + matchedConditions.insert(distributionName); + matchedConditions.insert(distributionName + "-" + distributionRelease); + } + for (int i = 0; i < conds.size(); ++i) { QByteArray c = conds.at(i); bool result = c.startsWith('!'); diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 222bdd3f39..e2f98c2f04 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -59,6 +59,9 @@ #include <QtTest/private/qbenchmark_p.h> #include <QtTest/private/cycle_p.h> #include <QtTest/private/qtestblacklist_p.h> +#if defined(HAVE_XCTEST) +#include <QtTest/private/qxctestlogger_p.h> +#endif #include <numeric> #include <algorithm> @@ -1530,6 +1533,11 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) QTestLog::LogMode logFormat = QTestLog::Plain; const char *logFilename = 0; +#if defined(Q_OS_MAC) && defined(HAVE_XCTEST) + if (QXcodeTestLogger::canLogTestProgress()) + logFormat = QTestLog::XCTest; +#endif + const char *testOptions = " New-style logging options:\n" " -o filename,format : Output results to file in the specified format\n" @@ -1782,10 +1790,14 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) } else if (strcmp(argv[i], "-vb") == 0) { QBenchmarkGlobalData::current->verboseOutput = true; -#ifdef Q_OS_WINRT +#if defined(Q_OS_WINRT) } else if (strncmp(argv[i], "-ServerName:", 12) == 0 || strncmp(argv[i], "-qdevel", 7) == 0) { continue; +#elif defined(Q_OS_MAC) && defined(HAVE_XCTEST) + } else if (int skip = QXcodeTestLogger::parseCommandLineArgument(argv[i])) { + i += (skip - 1); // Eating argv[i] with a continue counts towards skips + continue; #endif } else if (argv[i][0] == '-') { fprintf(stderr, "Unknown option: '%s'\n\n%s", argv[i], testOptions); diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp index 8a4afae447..59aeb27ffe 100644 --- a/src/testlib/qtestlog.cpp +++ b/src/testlib/qtestlog.cpp @@ -40,6 +40,10 @@ #include <QtTest/private/qcsvbenchmarklogger_p.h> #include <QtTest/private/qxunittestlogger_p.h> #include <QtTest/private/qxmltestlogger_p.h> +#if defined(HAVE_XCTEST) +#include <QtTest/private/qxctestlogger_p.h> +#endif + #include <QtCore/qatomic.h> #include <QtCore/qbytearray.h> #include <QtCore/QVariant> @@ -483,6 +487,11 @@ void QTestLog::addLogger(LogMode mode, const char *filename) case QTestLog::XunitXML: logger = new QXunitTestLogger(filename); break; +#if defined(HAVE_XCTEST) + case QTestLog::XCTest: + logger = new QXcodeTestLogger; + break; +#endif } QTEST_ASSERT(logger); QTest::TestLoggers::addLogger(logger); diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h index 75e39e8f3d..b4786b4904 100644 --- a/src/testlib/qtestlog_p.h +++ b/src/testlib/qtestlog_p.h @@ -55,7 +55,12 @@ class QRegularExpression; class Q_TESTLIB_EXPORT QTestLog { public: - enum LogMode { Plain = 0, XML, LightXML, XunitXML, CSV }; + enum LogMode { + Plain = 0, XML, LightXML, XunitXML, CSV, +#if defined(HAVE_XCTEST) + XCTest +#endif + }; static void enterTestFunction(const char* function); static void leaveTestFunction(); diff --git a/src/testlib/qxctestlogger.mm b/src/testlib/qxctestlogger.mm new file mode 100644 index 0000000000..576f46acea --- /dev/null +++ b/src/testlib/qxctestlogger.mm @@ -0,0 +1,501 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxctestlogger_p.h" + +#include <QtCore/qstring.h> + +#include <QtTest/private/qtestlog_p.h> +#include <QtTest/private/qtestresult_p.h> + +#import <XCTest/XCTest.h> + +// --------------------------------------------------------- + +@interface XCTestProbe (Private) ++ (BOOL)isTesting; ++ (void)runTests:(id)unusedArgument; ++ (NSString*)testScope; ++ (BOOL)isInverseTestScope; +@end + +@interface XCTestDriver : NSObject ++ (XCTestDriver*)sharedTestDriver; +@property (readonly, assign) NSObject *IDEConnection; +@end + +@interface XCTest (Private) +- (NSString *)nameForLegacyLogging; +@end + +#pragma GCC diagnostic push // Ignore XCTestProbe deprecation +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +// --------------------------------------------------------- + +@interface QtTestLibWrapper : XCTestCase +@end + +@interface QtTestLibTests : XCTestSuite ++ (XCTestSuiteRun*)testRun; +@end + +@interface QtTestLibTest : XCTestCase +@property (nonatomic, retain) NSString* testObjectName; +@property (nonatomic, retain) NSString* testFunctionName; +@end + +// --------------------------------------------------------- + +class ThreadBarriers +{ +public: + enum Barrier { + XCTestCanStartTesting, + XCTestHaveStarted, + QtTestsCanStartTesting, + QtTestsHaveCompleted, + XCTestsHaveCompleted, + BarrierCount + }; + + static ThreadBarriers *get() + { + static ThreadBarriers instance; + return &instance; + } + + static void initialize() { get(); } + + void wait(Barrier barrier) { dispatch_semaphore_wait(barriers[barrier], DISPATCH_TIME_FOREVER); } + void signal(Barrier barrier) { dispatch_semaphore_signal(barriers[barrier]); } + +private: + #define FOREACH_BARRIER(cmd) for (int i = 0; i < BarrierCount; ++i) { cmd } + + ThreadBarriers() { FOREACH_BARRIER(barriers[i] = dispatch_semaphore_create(0);) } + ~ThreadBarriers() { FOREACH_BARRIER(dispatch_release(barriers[i]);) } + + dispatch_semaphore_t barriers[BarrierCount]; +}; + +#define WAIT_FOR_BARRIER(b) ThreadBarriers::get()->wait(ThreadBarriers::b); +#define SIGNAL_BARRIER(b) ThreadBarriers::get()->signal(ThreadBarriers::b); + +// --------------------------------------------------------- + +@implementation QtTestLibWrapper + ++ (void)load +{ + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + + if (![XCTestProbe isTesting]) + return; + + if (!([NSDate timeIntervalSinceReferenceDate] > 0)) + qFatal("error: Device date '%s' is bad, likely set to update automatically. Please correct.", + [NSDate date].description.UTF8String); + + XCTestDriver *testDriver = nil; + if ([QtTestLibWrapper usingTestManager]) + testDriver = [XCTestDriver sharedTestDriver]; + + // Spawn off task to run test infrastructure on separate thread so that we can + // let main() execute like normal on the main thread. The queue will never be + // destroyed, so there's no point in trying to keep a proper retain count. + dispatch_async(dispatch_queue_create("io.qt.QTestLib.xctest-wrapper", DISPATCH_QUEUE_SERIAL), ^{ + Q_ASSERT(![NSThread isMainThread]); + [XCTestProbe runTests:nil]; + Q_UNREACHABLE(); + }); + + // Initialize barriers before registering exit handler so that the + // semaphores stay alive until after the exit handler completes. + ThreadBarriers::initialize(); + + // We register an exit handler so that we can intercept when main() completes + // and let the XCTest thread finish up. For main() functions that never started + // testing using QtTestLib we also need to signal that xcTestsCanStart. + atexit_b(^{ + Q_ASSERT([NSThread isMainThread]); + + // In case not started by startLogging + SIGNAL_BARRIER(XCTestCanStartTesting); + + // [XCTestProbe runTests:] ends up calling [XCTestProbe exitTests:] after + // all test suites have completed, which calls exit(). We use that to signal + // to the main thread that it's free to continue its exit handler. + atexit_b(^{ + Q_ASSERT(![NSThread isMainThread]); + SIGNAL_BARRIER(XCTestsHaveCompleted); + + // Block forever so that the main thread does all the cleanup + dispatch_semaphore_wait(dispatch_semaphore_create(0), DISPATCH_TIME_FOREVER); + }); + + SIGNAL_BARRIER(QtTestsHaveCompleted); + + // Ensure XCTest complets the top level tests suite + WAIT_FOR_BARRIER(XCTestsHaveCompleted); + }); + + // Let test driver (Xcode) connection setup complete before continuing + if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--use-testmanagerd"]) { + while (!testDriver.IDEConnection) + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } + + // Wait for our QtTestLib test suite to run before running main + WAIT_FOR_BARRIER(QtTestsCanStartTesting); + + // Prevent XCTestProbe from re-launching runTests on application startup + [[NSNotificationCenter defaultCenter] removeObserver:[XCTestProbe class] + name:[NSString stringWithFormat:@"%@DidFinishLaunchingNotification", + #if defined(Q_OS_OSX) + @"NSApplication" + #elif defined(Q_OS_IOS) + @"UIApplication" + #endif + ] + object:nil]; + + [autoreleasepool release]; +} + ++ (id)defaultTestSuite +{ + return [[QtTestLibTests alloc] initWithName:@"QtTestLib"]; +} + ++ (BOOL)usingTestManager +{ + return [[[NSProcessInfo processInfo] arguments] containsObject:@"--use-testmanagerd"]; +} + +@end + +// --------------------------------------------------------- + +static XCTestSuiteRun *s_qtTestSuiteRun = 0; + +@implementation QtTestLibTests + +- (void)performTest:(XCTestSuiteRun *)testSuiteRun +{ + Q_ASSERT(![NSThread isMainThread]); + + Q_ASSERT(!s_qtTestSuiteRun); + s_qtTestSuiteRun = testSuiteRun; + + SIGNAL_BARRIER(QtTestsCanStartTesting); + + // Wait for main() to complete, or a QtTestLib test to start, so we + // know if we should start the QtTestLib test suite. + WAIT_FOR_BARRIER(XCTestCanStartTesting); + + if (QXcodeTestLogger::isActive()) + [testSuiteRun start]; + + SIGNAL_BARRIER(XCTestHaveStarted); + + // All test reporting happens on main thread from now on. Wait until + // main() completes before allowing the XCTest thread to continue. + WAIT_FOR_BARRIER(QtTestsHaveCompleted); + + if ([testSuiteRun startDate]) + [testSuiteRun stop]; +} + ++ (XCTestSuiteRun*)testRun +{ + return s_qtTestSuiteRun; +} + +@end + +// --------------------------------------------------------- + +@implementation QtTestLibTest + +- (id)initWithInvocation:(NSInvocation *)invocation +{ + if (self = [super initWithInvocation:invocation]) { + // The test object name and function name are used by XCTest after QtTestLib has + // reset them, so we need to store them up front for each XCTestCase. + self.testObjectName = [NSString stringWithUTF8String:QTestResult::currentTestObjectName()]; + self.testFunctionName = [NSString stringWithUTF8String:QTestResult::currentTestFunction()]; + } + + return self; +} + +- (NSString *)testClassName +{ + return self.testObjectName; +} + +- (NSString *)testMethodName +{ + return self.testFunctionName; +} + +- (NSString *)nameForLegacyLogging +{ + NSString *name = [NSString stringWithFormat:@"%@::%@", [self testClassName], [self testMethodName]]; + if (QTestResult::currentDataTag() || QTestResult::currentGlobalDataTag()) { + const char *currentDataTag = QTestResult::currentDataTag() ? QTestResult::currentDataTag() : ""; + const char *globalDataTag = QTestResult::currentGlobalDataTag() ? QTestResult::currentGlobalDataTag() : ""; + const char *filler = (currentDataTag[0] && globalDataTag[0]) ? ":" : ""; + name = [name stringByAppendingString:[NSString stringWithFormat:@"(%s%s%s)", + globalDataTag, filler, currentDataTag]]; + } + + return name; +} + +@end + +// --------------------------------------------------------- + +bool QXcodeTestLogger::canLogTestProgress() +{ + return [XCTestProbe isTesting]; // FIXME: Exclude xctool +} + +int QXcodeTestLogger::parseCommandLineArgument(const char *argument) +{ + if (strncmp(argument, "-NS", 3) == 0 || strncmp(argument, "-Apple", 6) == 0) + return 2; // -NSTreatUnknownArgumentsAsOpen, -ApplePersistenceIgnoreState, etc, skip argument + else if (strcmp(argument, "--use-testmanagerd") == 0) + return 2; // Skip UID argument + else if (strncmp(argument, "-XCTest", 7) == 0) + return 2; // -XCTestInvertScope, -XCTest scope, etc, skip argument + else if (strcmp(argument + (strlen(argument) - 7), ".xctest") == 0) + return 1; // Skip test bundle + else + return 0; +} + +// --------------------------------------------------------- + +QXcodeTestLogger *QXcodeTestLogger::s_currentTestLogger = 0; + +// --------------------------------------------------------- + +QXcodeTestLogger::QXcodeTestLogger() + : QAbstractTestLogger(0) + , m_testRuns([[NSMutableArray arrayWithCapacity:2] retain]) + +{ + Q_ASSERT(!s_currentTestLogger); + s_currentTestLogger = this; +} + +QXcodeTestLogger::~QXcodeTestLogger() +{ + s_currentTestLogger = 0; + [m_testRuns release]; +} + +void QXcodeTestLogger::startLogging() +{ + SIGNAL_BARRIER(XCTestCanStartTesting); + + static dispatch_once_t onceToken; + dispatch_once (&onceToken, ^{ + WAIT_FOR_BARRIER(XCTestHaveStarted); + }); + + // Scope test object suite under top level QtTestLib test run + [m_testRuns addObject:[QtTestLibTests testRun]]; + + NSString *suiteName = [NSString stringWithUTF8String:QTestResult::currentTestObjectName()]; + pushTestRunForTest([XCTestSuite testSuiteWithName:suiteName], true); +} + +void QXcodeTestLogger::stopLogging() +{ + popTestRun(); +} + +static bool isTestFunctionInActiveScope(const char *function) +{ + static NSString *testScope = [XCTestProbe testScope]; + + enum TestScope { Unknown, All, None, Self, Selected }; + static TestScope activeScope = Unknown; + + if (activeScope == Unknown) { + if ([testScope isEqualToString:@"All"]) + activeScope = All; + else if ([testScope isEqualToString:@"None"]) + activeScope = None; + else if ([testScope isEqualToString:@"Self"]) + activeScope = Self; + else + activeScope = Selected; + } + + if (activeScope == All) + return true; + else if (activeScope == None) + return false; + else if (activeScope == Self) + return true; // Investigate + + Q_ASSERT(activeScope == Selected); + + static NSArray *forcedTests = [@[ @"initTestCase", @"initTestCase_data", @"cleanupTestCase" ] retain]; + if ([forcedTests containsObject:[NSString stringWithUTF8String:function]]) + return true; + + static NSArray *testsInScope = [[testScope componentsSeparatedByString:@","] retain]; + bool inScope = [testsInScope containsObject:[NSString stringWithFormat:@"%s/%s", + QTestResult::currentTestObjectName(), function]]; + + if ([XCTestProbe isInverseTestScope]) + inScope = !inScope; + + return inScope; +} + +void QXcodeTestLogger::enterTestFunction(const char *function) +{ + if (!isTestFunctionInActiveScope(function)) + QTestResult::setSkipCurrentTest(true); + + XCTest *test = [QtTestLibTest testCaseWithInvocation:nil]; + pushTestRunForTest(test, !QTestResult::skipCurrentTest()); +} + +void QXcodeTestLogger::leaveTestFunction() +{ + popTestRun(); +} + +void QXcodeTestLogger::addIncident(IncidentTypes type, const char *description, + const char *file, int line) +{ + XCTestRun *testRun = [m_testRuns lastObject]; + + // The 'expected' argument to recordFailureWithDescription refers to whether + // the failure was a regular failed assertion, or an unexpected exception, + // so in our case it's always 'YES', and we need to explicitly ignore XFail. + if (type == QAbstractTestLogger::XFail) { + QTestCharBuffer buf; + NSString *testCaseName = [[testRun test] nameForLegacyLogging]; + QTest::qt_asprintf(&buf, "Test Case '%s' failed expectedly (%s).\n", + [testCaseName UTF8String], description); + outputString(buf.constData()); + return; + } + + if (type == QAbstractTestLogger::Pass) { + // We ignore non-data passes, as we're already reporting that as part of the + // normal test case start/stop cycle. + if (!(QTestResult::currentDataTag() || QTestResult::currentGlobalDataTag())) + return; + + QTestCharBuffer buf; + NSString *testCaseName = [[testRun test] nameForLegacyLogging]; + QTest::qt_asprintf(&buf, "Test Case '%s' passed.\n", [testCaseName UTF8String]); + outputString(buf.constData()); + return; + } + + // FIXME: Handle blacklisted tests + + if (!file || !description) + return; // Or report? + + [testRun recordFailureWithDescription:[NSString stringWithUTF8String:description] + inFile:[NSString stringWithUTF8String:file] atLine:line expected:YES]; +} + +void QXcodeTestLogger::addMessage(MessageTypes type, const QString &message, + const char *file, int line) +{ + QTestCharBuffer buf; + + if (QTestLog::verboseLevel() > 0 && (file && line)) { + QTest::qt_asprintf(&buf, "%s:%d: ", file, line); + outputString(buf.constData()); + } + + if (type == QAbstractTestLogger::Skip) { + XCTestRun *testRun = [m_testRuns lastObject]; + NSString *testCaseName = [[testRun test] nameForLegacyLogging]; + QTest::qt_asprintf(&buf, "Test Case '%s' skipped (%s).\n", + [testCaseName UTF8String], message.toUtf8().constData()); + } else { + QTest::qt_asprintf(&buf, "%s\n", message.toUtf8().constData()); + } + + outputString(buf.constData()); +} + +void QXcodeTestLogger::addBenchmarkResult(const QBenchmarkResult &result) +{ + Q_UNUSED(result); +} + +void QXcodeTestLogger::pushTestRunForTest(XCTest *test, bool start) +{ + XCTestRun *testRun = [[test testRunClass] testRunWithTest:test]; + [m_testRuns addObject:testRun]; + + if (start) + [testRun start]; +} + +XCTestRun *QXcodeTestLogger::popTestRun() +{ + XCTestRun *testRun = [[m_testRuns lastObject] retain]; + [m_testRuns removeLastObject]; + + if ([testRun startDate]) + [testRun stop]; + + [[m_testRuns lastObject] addTestRun:testRun]; + [testRun release]; + + return testRun; +} + +bool QXcodeTestLogger::isActive() +{ + return s_currentTestLogger; +} + +#pragma GCC diagnostic pop diff --git a/src/testlib/qxctestlogger_p.h b/src/testlib/qxctestlogger_p.h new file mode 100644 index 0000000000..95ad1374bc --- /dev/null +++ b/src/testlib/qxctestlogger_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXCTESTLOGGER_P_H +#define QXCTESTLOGGER_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. +// + +#include <QtTest/private/qabstracttestlogger_p.h> + +#include <dispatch/dispatch.h> + +Q_FORWARD_DECLARE_OBJC_CLASS(XCTest); +Q_FORWARD_DECLARE_OBJC_CLASS(XCTestRun); +Q_FORWARD_DECLARE_OBJC_CLASS(NSMutableArray); + +QT_BEGIN_NAMESPACE + +class QXcodeTestLogger : public QAbstractTestLogger +{ +public: + QXcodeTestLogger(); + ~QXcodeTestLogger() Q_DECL_OVERRIDE; + + void startLogging() Q_DECL_OVERRIDE; + void stopLogging() Q_DECL_OVERRIDE; + + void enterTestFunction(const char *function) Q_DECL_OVERRIDE; + void leaveTestFunction() Q_DECL_OVERRIDE; + + void addIncident(IncidentTypes type, const char *description, + const char *file = 0, int line = 0) Q_DECL_OVERRIDE; + + void addMessage(MessageTypes type, const QString &message, + const char *file = 0, int line = 0) Q_DECL_OVERRIDE; + + void addBenchmarkResult(const QBenchmarkResult &result) Q_DECL_OVERRIDE; + + static bool canLogTestProgress(); + static int parseCommandLineArgument(const char *argument); + + static bool isActive(); + +private: + void pushTestRunForTest(XCTest *test, bool start); + XCTestRun *popTestRun(); + + NSMutableArray *m_testRuns; + + static QXcodeTestLogger *s_currentTestLogger; +}; + + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/testlib.pro b/src/testlib/testlib.pro index dbcc588d10..841d913105 100644 --- a/src/testlib/testlib.pro +++ b/src/testlib/testlib.pro @@ -76,6 +76,25 @@ wince*::LIBS += libcmt.lib \ mac { LIBS += -framework Security osx: LIBS += -framework ApplicationServices -framework IOKit + + # XCTest support + !lessThan(QMAKE_XCODE_VERSION, "6.0") { + OBJECTIVE_SOURCES += qxctestlogger.mm + HEADERS += qxctestlogger_p.h + + DEFINES += HAVE_XCTEST + LIBS += -framework Foundation + + load(sdk) + platform_dev_frameworks_path = $${QMAKE_MAC_SDK_PLATFORM_PATH}/Developer/Library/Frameworks + + # We can't put this path into LIBS (so that it propagates to the prl file), as we + # don't know yet if the target that links to testlib will build under Xcode or not. + # The corresponding flags for the target lives in xctest.prf, where we do know. + QMAKE_LFLAGS += -F$${platform_dev_frameworks_path} -weak_framework XCTest + QMAKE_OBJECTIVE_CFLAGS += -F$${platform_dev_frameworks_path} + MODULE_CONFIG += xctest + } } load(qt_module) |