// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #import #import #import #import "../../lib/cstring.h" #import "../../lib/vstguiinit.h" #import "unittests.h" #import #import #import #import using namespace VSTGUI::UnitTest; //------------------------------------------------------------------------ void setupFunc (id self, SEL _cmd) { auto cls = [self class]; std::string clsName (class_getName (cls) + 7); auto testSuite = UnitTestRegistry::instance ().find (clsName); assert (testSuite); auto ivar = class_getInstanceVariable ([self class], "_testSuite"); object_setIvar (self, ivar, reinterpret_cast (testSuite)); } //------------------------------------------------------------------------ void testFunc (id self, SEL _cmd) { auto ivar = class_getInstanceVariable ([self class], "_testSuite"); auto testSuite = reinterpret_cast (object_getIvar (self, ivar)); auto name = std::string (sel_getName (_cmd) + 4); auto it = std::find_if (testSuite->begin (), testSuite->end (), [&] (auto& pair) { return pair.first == name; }); assert (it != testSuite->end ()); struct TestContext : Context { TestContext (TestSuite& suite) : suite (suite) {} void printRaw (const char* str) override { text += str; } std::any& storage () override { return suite.getStorage (); } TestSuite& suite; std::string text; } context (*testSuite); size_t assertLineNo = 0; std::string assertFilePath; bool result = true; try { if (auto setup = testSuite->setup ()) setup (&context); try { it->second (&context); } catch (...) { if (auto teardown = testSuite->teardown ()) teardown (&context); throw; } if (auto teardown = testSuite->teardown ()) teardown (&context); } catch (const error& exc) { result = false; context.print ("%s", exc.what () ? exc.what () : "unknown"); assertLineNo = exc.lineNo; assertFilePath = exc.filePath; } catch (const std::logic_error& exc) { result = false; context.print ("%s", exc.what () ? exc.what () : "unknown"); } catch (const std::exception& exc) { result = false; context.print ("Exception: %s", exc.what () ? exc.what () : "unknown"); } if (!result) { #ifdef MAC_OS_VERSION_11_0 auto sourceCodeLocation = [[[XCTSourceCodeLocation alloc] initWithFilePath:[NSString stringWithUTF8String:assertFilePath.data ()] lineNumber:assertLineNo] autorelease]; auto sourceCodeContext = [[[XCTSourceCodeContext alloc] initWithLocation:sourceCodeLocation] autorelease]; auto issue = [[[XCTIssue alloc] initWithType:XCTIssueTypeAssertionFailure compactDescription:[NSString stringWithUTF8String:context.text.data ()] detailedDescription:nullptr sourceCodeContext:sourceCodeContext associatedError:nullptr attachments:@[]] autorelease]; [self recordIssue:issue]; #else [self recordFailureWithDescription:[NSString stringWithUTF8String:context.text.data ()] inFile:[NSString stringWithUTF8String:assertFilePath.data ()] atLine:assertLineNo expected:NO]; #endif } } //------------------------------------------------------------------------ @interface VSTGUITestCaseFactory : XCTestCase @end @implementation VSTGUITestCaseFactory //------------------------------------------------------------------------ + (void)initialize { VSTGUI::setAssertionHandler ( [] (const char* file, const char* line, const char* condition, const char* desc) { size_t lineNo = 0; if (line) { if (auto l = VSTGUI::UTF8StringView (line).toNumber ()) lineNo = *l; } std::string text; if (desc) { text = desc; } else { text = "Assertion: '"; text += condition; text += "' failed."; } throw VSTGUI::UnitTest::error (file, lineNo, text.data ()); }); VSTGUI::init (CFBundleGetMainBundle ()); auto baseClass = objc_getClass ("XCTestCase"); for (auto& testCase : UnitTestRegistry::instance ()) { std::string className = "VSTGUI_" + testCase.getName (); auto cls = objc_allocateClassPair (baseClass, className.data (), 0); if (!cls) { NSLog (@"Duplicate TestCase name: %s", testCase.getName ().data ()); continue; } class_addIvar (cls, "_testSuite", sizeof (void*), (uint8_t)log2 (sizeof (void*)), @encode (void*)); class_addMethod (cls, @selector (setUp), reinterpret_cast (setupFunc), "v@:"); for (auto& test : testCase) { std::string name = "test" + test.first; auto sel = sel_registerName (name.data ()); if (!class_addMethod (cls, sel, reinterpret_cast (testFunc), "v@:")) { NSLog (@"Duplicate Method name: %s [%s]", name.data (), className.data ()); } } objc_registerClassPair (cls); } } @end