6#include <QtCore/qstring.h>
8#include <QtTest/private/qtestlog_p.h>
9#include <QtTest/private/qtestresult_p.h>
11#import <XCTest/XCTest.h>
18@interface XCTestProbe (Private)
20+ (
void)runTests:(id)unusedArgument;
22+ (BOOL)isInverseTestScope;
25@interface XCTestDriver : NSObject
26+ (XCTestDriver*)sharedTestDriver;
27@property (readonly, assign) NSObject *IDEConnection;
30@interface XCTest (Private)
31- (NSString *)nameForLegacyLogging;
36QT_WARNING_DISABLE_DEPRECATED
40@interface QtTestLibWrapper : XCTestCase
43@interface QtTestLibTests : XCTestSuite
44+ (XCTestSuiteRun*)testRun;
47@interface QtTestLibTest : XCTestCase
48@property (nonatomic, retain) NSString* testObjectName;
49@property (nonatomic, retain) NSString* testFunctionName;
58 XCTestCanStartTesting,
60 QtTestsCanStartTesting,
66 static ThreadBarriers *get()
68 static ThreadBarriers instance;
72 static void initialize() { get(); }
74 void wait(Barrier barrier) { dispatch_semaphore_wait(barriers[barrier], DISPATCH_TIME_FOREVER); }
75 void signal(Barrier barrier) { dispatch_semaphore_signal(barriers[barrier]); }
78 #define FOREACH_BARRIER(cmd) for (int i = 0
; i < BarrierCount; ++i) { cmd }
80 ThreadBarriers() {
FOREACH_BARRIER(barriers[i] = dispatch_semaphore_create(0);) }
83 dispatch_semaphore_t barriers[BarrierCount];
86#define WAIT_FOR_BARRIER(b) ThreadBarriers::get()->wait(ThreadBarriers::b);
87#define SIGNAL_BARRIER(b) ThreadBarriers::get()->signal(ThreadBarriers::b);
91@implementation QtTestLibWrapper
95 NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
97 if (![XCTestProbe isTesting])
100 if (Q_UNLIKELY(!([NSDate timeIntervalSinceReferenceDate] > 0)))
101 qFatal(
"error: Device date '%s' is bad, likely set to update automatically. Please correct.",
102 [[NSDate date] description].UTF8String);
104 XCTestDriver *testDriver = nil;
105 if ([QtTestLibWrapper usingTestManager])
106 testDriver = [XCTestDriver sharedTestDriver];
111 dispatch_async(dispatch_queue_create(
"io.qt.QTestLib.xctest-wrapper", DISPATCH_QUEUE_SERIAL), ^{
112 Q_ASSERT(![NSThread isMainThread]);
113 [XCTestProbe runTests:nil];
119 ThreadBarriers::initialize();
125 Q_ASSERT([NSThread isMainThread]);
134 Q_ASSERT(![NSThread isMainThread]);
138 dispatch_semaphore_wait(dispatch_semaphore_create(0), DISPATCH_TIME_FOREVER);
148 if ([[[NSProcessInfo processInfo] arguments] containsObject:@
"--use-testmanagerd"]) {
149 while (!testDriver.IDEConnection)
150 [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
157 [[NSNotificationCenter defaultCenter] removeObserver:[XCTestProbe
class]
158 name:[NSString stringWithFormat:@
"%@DidFinishLaunchingNotification",
159 #if defined(Q_OS_MACOS)
167 [autoreleasepool release];
170+ (QTestLibTests *)defaultTestSuite
172 return [[QtTestLibTests alloc] initWithName:@
"QtTestLib"];
175+ (BOOL)usingTestManager
177 return [[[NSProcessInfo processInfo] arguments] containsObject:@
"--use-testmanagerd"];
184static XCTestSuiteRun *s_qtTestSuiteRun =
nullptr;
186@implementation QtTestLibTests
188- (
void)performTest:(XCTestSuiteRun *)testSuiteRun
190 Q_ASSERT(![NSThread isMainThread]);
192 Q_ASSERT(!s_qtTestSuiteRun);
193 s_qtTestSuiteRun = testSuiteRun;
201 if (QXcodeTestLogger::isActive())
202 [testSuiteRun start];
210 if ([testSuiteRun startDate])
214+ (XCTestSuiteRun*)testRun
216 return s_qtTestSuiteRun;
223@implementation QtTestLibTest
225- (instancetype)initWithInvocation:(NSInvocation *)invocation
227 if (self = [super initWithInvocation:invocation]) {
230 self.testObjectName = [NSString stringWithUTF8String:QTestResult::currentTestObjectName()];
231 self.testFunctionName = [NSString stringWithUTF8String:QTestResult::currentTestFunction()];
239 return self.testObjectName;
244 return self.testFunctionName;
249 NSString *name = [NSString stringWithFormat:@
"%@::%@", [self testClassName], [self testMethodName]];
250 if (QTestResult::currentDataTag() || QTestResult::currentGlobalDataTag()) {
251 const char *currentDataTag = QTestResult::currentDataTag() ? QTestResult::currentDataTag() :
"";
252 const char *globalDataTag = QTestResult::currentGlobalDataTag() ? QTestResult::currentGlobalDataTag() :
"";
253 const char *filler = (currentDataTag[0] && globalDataTag[0]) ?
":" :
"";
254 name = [name stringByAppendingString:[NSString stringWithFormat:@
"(%s%s%s)",
255 globalDataTag, filler, currentDataTag]];
265bool QXcodeTestLogger::canLogTestProgress()
267 return [XCTestProbe isTesting];
270int QXcodeTestLogger::parseCommandLineArgument(
const char *argument)
272 if (strncmp(argument,
"-NS", 3) == 0 || strncmp(argument,
"-Apple", 6) == 0)
274 else if (strcmp(argument,
"--use-testmanagerd") == 0)
276 else if (strncmp(argument,
"-XCTest", 7) == 0)
278 else if (strcmp(argument + (strlen(argument) - 7),
".xctest") == 0)
286QXcodeTestLogger *QXcodeTestLogger::s_currentTestLogger = 0;
290QXcodeTestLogger::QXcodeTestLogger()
291 : QAbstractTestLogger(0)
292 , m_testRuns([[NSMutableArray<XCTestRun *> arrayWithCapacity:2] retain])
295 Q_ASSERT(!s_currentTestLogger);
296 s_currentTestLogger =
this;
299QXcodeTestLogger::~QXcodeTestLogger()
301 s_currentTestLogger = 0;
302 [m_testRuns release];
305void QXcodeTestLogger::startLogging()
309 static dispatch_once_t onceToken;
310 dispatch_once (&onceToken, ^{
315 [m_testRuns addObject:[QtTestLibTests testRun]];
317 NSString *suiteName = [NSString stringWithUTF8String:QTestResult::currentTestObjectName()];
318 pushTestRunForTest([XCTestSuite testSuiteWithName:suiteName],
true);
321void QXcodeTestLogger::stopLogging()
326static bool isTestFunctionInActiveScope(
const char *function)
328 static NSString *testScope = [XCTestProbe testScope];
330 enum TestScope { Unknown, All, None, Self, Selected };
331 static TestScope activeScope = Unknown;
333 if (activeScope == Unknown) {
334 if ([testScope isEqualToString:@
"All"])
336 else if ([testScope isEqualToString:@
"None"])
338 else if ([testScope isEqualToString:@
"Self"])
341 activeScope = Selected;
344 if (activeScope == All)
346 else if (activeScope == None)
348 else if (activeScope == Self)
351 Q_ASSERT(activeScope == Selected);
353 static NSArray<NSString *> *forcedTests = [@[ @
"initTestCase", @
"initTestCase_data", @
"cleanupTestCase" ] retain];
354 if ([forcedTests containsObject:[NSString stringWithUTF8String:function]])
357 static NSArray<NSString *> *testsInScope = [[testScope componentsSeparatedByString:@
","] retain];
358 bool inScope = [testsInScope containsObject:[NSString stringWithFormat:@
"%s/%s",
359 QTestResult::currentTestObjectName(), function]];
361 if ([XCTestProbe isInverseTestScope])
367void QXcodeTestLogger::enterTestFunction(
const char *function)
369 if (!isTestFunctionInActiveScope(function))
370 QTestResult::setSkipCurrentTest(
true);
372 XCTest *test = [QtTestLibTest testCaseWithInvocation:nil];
373 pushTestRunForTest(test, !QTestResult::skipCurrentTest());
376void QXcodeTestLogger::leaveTestFunction()
381void QXcodeTestLogger::addIncident(IncidentTypes type,
const char *description,
382 const char *file,
int line)
384 XCTestRun *testRun = [m_testRuns lastObject];
389 if (type == QAbstractTestLogger::XFail) {
391 NSString *testCaseName = [[testRun test] nameForLegacyLogging];
392 QTest::qt_asprintf(&buf,
"Test Case '%s' failed expectedly (%s).\n",
393 [testCaseName UTF8String], description);
394 outputString(buf.constData());
398 if (type == QAbstractTestLogger::Pass) {
401 if (!(QTestResult::currentDataTag() || QTestResult::currentGlobalDataTag()))
405 NSString *testCaseName = [[testRun test] nameForLegacyLogging];
406 QTest::qt_asprintf(&buf,
"Test Case '%s' passed.\n", [testCaseName UTF8String]);
407 outputString(buf.constData());
413 if (!file || !description)
416 [testRun recordFailureWithDescription:[NSString stringWithUTF8String:description]
417 inFile:[NSString stringWithUTF8String:file] atLine:line expected:YES];
420void QXcodeTestLogger::addMessage(MessageTypes type,
const QString &message,
421 const char *file,
int line)
425 if (QTestLog::verboseLevel() > 0 && (file && line)) {
426 QTest::qt_asprintf(&buf,
"%s:%d: ", file, line);
427 outputString(buf.constData());
430 if (type == QAbstractTestLogger::Skip) {
431 XCTestRun *testRun = [m_testRuns lastObject];
432 NSString *testCaseName = [[testRun test] nameForLegacyLogging];
433 QTest::qt_asprintf(&buf,
"Test Case '%s' skipped (%s).\n",
434 [testCaseName UTF8String], message.toUtf8().constData());
436 QTest::qt_asprintf(&buf,
"%s\n", message.toUtf8().constData());
439 outputString(buf.constData());
442void QXcodeTestLogger::addBenchmarkResult(
const QBenchmarkResult &result)
447void QXcodeTestLogger::pushTestRunForTest(XCTest *test,
bool start)
449 XCTestRun *testRun = [[test testRunClass] testRunWithTest:test];
450 [m_testRuns addObject:testRun];
456XCTestRun *QXcodeTestLogger::popTestRun()
458 XCTestRun *testRun = [[m_testRuns lastObject] retain];
459 [m_testRuns removeLastObject];
461 if ([testRun startDate])
464 [[m_testRuns lastObject] addTestRun:testRun];
470bool QXcodeTestLogger::isActive()
472 return s_currentTestLogger;
Q_FORWARD_DECLARE_OBJC_CLASS(NSString)
#define WAIT_FOR_BARRIER(b)
#define SIGNAL_BARRIER(b)
#define FOREACH_BARRIER(cmd)