Index: .fossil-settings/clean-glob ================================================================== --- .fossil-settings/clean-glob +++ .fossil-settings/clean-glob @@ -30,11 +30,14 @@ src/bridge/Info.plist src/libobjfw.* src/objfw-defs.h src/runtime/Info.plist src/runtime/libobjfwrt.* +src/test/Info.plist +src/test/libobjfwtest.* src/tls/Info.plist +src/tls/libobjfwtls.* tests/DerivedData tests/EBOOT.PBP tests/Info.plist tests/PARAM.SFO tests/objc_sync/objc_sync Index: .fossil-settings/ignore-glob ================================================================== --- .fossil-settings/ignore-glob +++ .fossil-settings/ignore-glob @@ -32,11 +32,14 @@ src/bridge/Info.plist src/libobjfw.* src/objfw-defs.h src/runtime/Info.plist src/runtime/libobjfwrt.* +src/test/Info.plist +src/test/libobjfwtest.* src/tls/Info.plist +src/tls/libobjfwtls.* tests/DerivedData tests/EBOOT.PBP tests/Info.plist tests/PARAM.SFO tests/iOS.xcodeproj/*.pbxuser Index: .gitignore ================================================================== --- .gitignore +++ .gitignore @@ -32,11 +32,14 @@ src/bridge/Info.plist src/libobjfw.* src/objfw-defs.h src/runtime/Info.plist src/runtime/libobjfwrt.* +src/test/Info.plist +src/test/libobjfwtest.* src/tls/Info.plist +src/tls/libobjfwtls.* tests/DerivedData tests/EBOOT.PBP tests/Info.plist tests/PARAM.SFO tests/iOS.xcodeproj/*.pbxuser Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -2374,10 +2374,11 @@ AC_CONFIG_FILES([ buildsys.mk extra.mk src/Info.plist + src/test/Info.plist tests/Info.plist utils/objfw-config ]) AC_CONFIG_HEADERS([config.h src/objfw-defs.h]) AC_OUTPUT Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -1,9 +1,9 @@ include ../extra.mk SUBDIRS = ${RUNTIME} exceptions encodings forwarding -SUBDIRS_AFTER = ${BRIDGE} ${TLS} +SUBDIRS_AFTER = ${BRIDGE} ${TLS} test DISTCLEAN = Info.plist objfw-defs.h SHARED_LIB = ${OBJFW_SHARED_LIB} STATIC_LIB = ${OBJFW_STATIC_LIB} FRAMEWORK = ${OBJFW_FRAMEWORK} ADDED src/test/Info.plist.in Index: src/test/Info.plist.in ================================================================== --- src/test/Info.plist.in +++ src/test/Info.plist.in @@ -0,0 +1,22 @@ + + + + + CFBundleExecutable + ObjFWTest + CFBundleName + ObjFWTest + CFBundleIdentifier + im.nil.objfw.test + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleVersion + @BUNDLE_VERSION@ + CFBundleShortVersionString + @BUNDLE_SHORT_VERSION@ + MinimumOSVersion + 9.0 + + ADDED src/test/Makefile Index: src/test/Makefile ================================================================== --- src/test/Makefile +++ src/test/Makefile @@ -0,0 +1,50 @@ +include ../../extra.mk + +DISTCLEAN = Info.plist + +STATIC_LIB = libobjfwtest.a + +SRCS = OTAssert.m \ + OTTestCase.m +INCLUDES := ${SRCS:.m=.h} \ + ObjFWTest.h +SRCS += OTAppDelegate.m \ + OTAssertionFailedException.m + +includesubdir = ObjFWTest + +include ../../buildsys.mk + +CPPFLAGS += -I. \ + -I.. \ + -I../.. \ + -I../exceptions \ + -I../runtime \ + -DOBJFWTEST_LOCAL_INCLUDES +LD = ${OBJC} +FRAMEWORK_LIBS := -F.. \ + -framework ObjFW \ + -F../runtime \ + ${RUNTIME_FRAMEWORK_LIBS} \ + ${LIBS} +LIBS := -L.. -lobjfw -L../runtime ${RUNTIME_LIBS} ${LIBS} + +install-extra: + i=ObjFWTest.oc; \ + ${INSTALL_STATUS}; \ + if ${MKDIR_P} ${libdir}/objfw-config && ${INSTALL} -m 644 $$i ${libdir}/objfw-config/$$i; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi + +uninstall-extra: + i=ObjFWTest.oc; \ + if test -f ${libdir}/objfw-config/$$i; then \ + if rm -f ${libdir}/objfw-config/$$i; then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi; \ + rmdir ${libdir}/objfw-config >/dev/null 2>&1 || true ADDED src/test/OTAppDelegate.h Index: src/test/OTAppDelegate.h ================================================================== --- src/test/OTAppDelegate.h +++ src/test/OTAppDelegate.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFApplication.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OTAppDelegate: OFObject +@end + +OF_ASSUME_NONNULL_END ADDED src/test/OTAppDelegate.m Index: src/test/OTAppDelegate.m ================================================================== --- src/test/OTAppDelegate.m +++ src/test/OTAppDelegate.m @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OTAppDelegate.h" + +#import "OFSet.h" +#import "OFValue.h" + +#import "OTTestCase.h" + +#import "OTAssertionFailedException.h" + +OF_APPLICATION_DELEGATE(OTAppDelegate) + +@implementation OTAppDelegate +- (OFSet OF_GENERIC(Class) *)testClasses +{ + Class *classes = objc_copyClassList(NULL); + OFMutableSet *testClasses; + + if (classes == NULL) + return nil; + + @try { + testClasses = [OFMutableSet set]; + + for (Class *iter = classes; *iter != Nil; iter++) + if ([*iter isSubclassOfClass: [OTTestCase class]]) + [testClasses addObject: *iter]; + } @finally { + OFFreeMemory(classes); + } + + [testClasses removeObject: [OTTestCase class]]; + + [testClasses makeImmutable]; + return testClasses; +} + +- (OFSet OF_GENERIC(OFValue *) *)testsInClass: (Class)class +{ + Method *methods = class_copyMethodList(class, NULL); + OFMutableSet *tests; + + if (methods == NULL) + return nil; + + @try { + tests = [OFMutableSet set]; + + for (Method *iter = methods; *iter != NULL; iter++) { + SEL selector = method_getName(*iter); + + if (selector == NULL) + continue; + + if (strncmp(sel_getName(selector), "test", 4) == 0) + [tests addObject: + [OFValue valueWithPointer: selector]]; + } + } @finally { + OFFreeMemory(methods); + } + + [tests makeImmutable]; + return tests; +} + +- (void)applicationDidFinishLaunching: (OFNotification *)notification +{ + OFSet OF_GENERIC(Class) *testClasses = [self testClasses]; + + for (Class class in testClasses) { + for (OFValue *test in [self testsInClass: class]) { + void *pool = objc_autoreleasePoolPush(); + OTTestCase *instance = + [[[class alloc] init] autorelease]; + + @try { + [instance setUp]; + [instance performSelector: test.pointerValue]; + } @catch (OTAssertionFailedException *e) { + /* + * If an assertion during -[setUp], don't run + * the test. + * If an assertion fails during a test, abort + * the test. + */ + } + @try { + [instance tearDown]; + } @catch (OTAssertionFailedException *e) { + /* + * If an assertion fails during -[tearDown], + * abort the tear down. + */ + } + + objc_autoreleasePoolPop(pool); + } + } + + [OFApplication terminate]; +} +@end ADDED src/test/OTAssert.h Index: src/test/OTAssert.h ================================================================== --- src/test/OTAssert.h +++ src/test/OTAssert.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#define OTAssert(cond, ...) \ + OTAssertImpl(self, _cmd, cond, @#cond, @__FILE__, __LINE__, \ + ## __VA_ARGS__, nil) +#define OTAssertTrue(cond, ...) OTAssert(cond == true, ## __VA_ARGS__) +#define OTAssertFalse(cond, ...) OTAssert(cond == false, ## __VA_ARGS__) +#define OTAssertEqual(a, b, ...) OTAssert(a == b, ## __VA_ARGS__) +#define OTAssertNotEqual(a, b, ...) OTAssert(a != b, ## __VA_ARGS__) +#define OTAssertEqualObjects(a, b, ...) OTAssert([a isEqual: b], ## __VA_ARGS__) +#define OTAssertNotEqualObjects(a, b, ...) \ + OTAssert(![a isEqual: b], ## __VA_ARGS__) + +#ifdef __cplusplus +extern "C" { +#endif +extern void OTAssertImpl(id testCase, SEL test, bool condition, OFString *check, + OFString *file, size_t line, ...); +#ifdef __cplusplus +} +#endif ADDED src/test/OTAssert.m Index: src/test/OTAssert.m ================================================================== --- src/test/OTAssert.m +++ src/test/OTAssert.m @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFColor.h" +#import "OFStdIOStream.h" +#import "OFString.h" + +#import "OTAssertionFailedException.h" + +void +OTAssertImpl(id testCase, SEL test, bool condition, OFString *check, + OFString *file, size_t line, ...) +{ + void *pool; + va_list arguments; + OFConstantString *format; + OFString *message = nil; + + if (condition) + return; + + pool = objc_autoreleasePoolPush(); + + va_start(arguments, line); + format = va_arg(arguments, OFConstantString *); + + if (format != nil) + message = [[[OFString alloc] + initWithFormat: format + arguments: arguments] autorelease]; + + va_end(arguments); + + [OFStdErr setForegroundColor: [OFColor red]]; + [OFStdErr writeFormat: @"-[%@ %s]: Condition failed: %@%@%@\n", + [testCase className], sel_getName(test), + check, (message != nil ? @": " : @""), + (message != nil ? message : @"")]; + [OFStdErr reset]; + + objc_autoreleasePoolPop(pool); + + @throw [OTAssertionFailedException exception]; +} ADDED src/test/OTAssertionFailedException.h Index: src/test/OTAssertionFailedException.h ================================================================== --- src/test/OTAssertionFailedException.h +++ src/test/OTAssertionFailedException.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFException.h" + +@interface OTAssertionFailedException: OFException +@end ADDED src/test/OTAssertionFailedException.m Index: src/test/OTAssertionFailedException.m ================================================================== --- src/test/OTAssertionFailedException.m +++ src/test/OTAssertionFailedException.m @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OTAssertionFailedException.h" + +@implementation OTAssertionFailedException +@end ADDED src/test/OTTestCase.h Index: src/test/OTTestCase.h ================================================================== --- src/test/OTTestCase.h +++ src/test/OTTestCase.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#ifdef OBJFWTEST_LOCAL_INCLUDES +# import "OFObject.h" +#else +# import +#endif + +OF_ASSUME_NONNULL_BEGIN + +@interface OTTestCase: OFObject +- (void)setUp; +- (void)tearDown; +@end + +OF_ASSUME_NONNULL_END ADDED src/test/OTTestCase.m Index: src/test/OTTestCase.m ================================================================== --- src/test/OTTestCase.m +++ src/test/OTTestCase.m @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OTTestCase.h" + +@implementation OTTestCase: OFObject +- (void)setUp +{ +} + +- (void)tearDown +{ +} +@end ADDED src/test/ObjFWTest.h Index: src/test/ObjFWTest.h ================================================================== --- src/test/ObjFWTest.h +++ src/test/ObjFWTest.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OTTestCase.h" +#import "OTAssert.h" ADDED src/test/ObjFWTest.oc Index: src/test/ObjFWTest.oc ================================================================== --- src/test/ObjFWTest.oc +++ src/test/ObjFWTest.oc @@ -0,0 +1,3 @@ +package_format 1 +LIBS="-lobjfwtest $LIBS" +FRAMEWORK_LIBS="-framework ObjFWTest $FRAMEWORK_LIBS"