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"