Index: .fossil-settings/clean-glob ================================================================== --- .fossil-settings/clean-glob +++ .fossil-settings/clean-glob @@ -34,24 +34,32 @@ src/runtime/libobjfwrt.* src/test/libobjfwtest.a src/tls/Info.plist src/tls/libobjfwtls.* tests/DerivedData +tests/EBOOT.PBP tests/EBOOT.PBP tests/Info.plist +tests/PARAM.SFO tests/PARAM.SFO tests/objc_sync/objc_sync tests/plugin/Info.plist tests/subprocess/subprocess tests/terminal/terminal_tests tests/testfile_bin.m tests/testfile_ini.m tests/tests +tests/tests +tests/tests.3dsx tests/tests.3dsx tests/tests.arm9 +tests/tests.arm9 +tests/tests.nds tests/tests.nds tests/tests.nro +tests/tests.nro +tests/tests.rpx tests/tests.rpx utils/objfw-config utils/objfw-new/objfw-new utils/ofarc/ofarc utils/ofdns/ofdns Index: .fossil-settings/ignore-glob ================================================================== --- .fossil-settings/ignore-glob +++ .fossil-settings/ignore-glob @@ -36,12 +36,14 @@ src/runtime/libobjfwrt.* src/test/libobjfwtest.a src/tls/Info.plist src/tls/libobjfwtls.* tests/DerivedData +tests/EBOOT.PBP tests/EBOOT.PBP tests/Info.plist +tests/PARAM.SFO tests/PARAM.SFO tests/iOS.xcodeproj/*.pbxuser tests/iOS.xcodeproj/project.xcworkspace tests/iOS.xcodeproj/xcuserdata tests/objc_sync/objc_sync @@ -49,16 +51,22 @@ tests/subprocess/subprocess tests/terminal/terminal_tests tests/testfile_bin.m tests/testfile_ini.m tests/tests +tests/tests +tests/tests.3dsx tests/tests.3dsx tests/tests.arm9 +tests/tests.arm9 +tests/tests.nds tests/tests.nds tests/tests.nro +tests/tests.nro +tests/tests.rpx tests/tests.rpx utils/objfw-config utils/objfw-new/objfw-new utils/ofarc/ofarc utils/ofdns/ofdns utils/ofhash/ofhash utils/ofhttp/ofhttp Index: .gitignore ================================================================== --- .gitignore +++ .gitignore @@ -36,12 +36,14 @@ src/runtime/libobjfwrt.* src/test/libobjfwtest.a src/tls/Info.plist src/tls/libobjfwtls.* tests/DerivedData +tests/EBOOT.PBP tests/EBOOT.PBP tests/Info.plist +tests/PARAM.SFO tests/PARAM.SFO tests/iOS.xcodeproj/*.pbxuser tests/iOS.xcodeproj/project.xcworkspace tests/iOS.xcodeproj/xcuserdata tests/objc_sync/objc_sync @@ -49,16 +51,22 @@ tests/subprocess/subprocess tests/terminal/terminal_tests tests/testfile_bin.m tests/testfile_ini.m tests/tests +tests/tests +tests/tests.3dsx tests/tests.3dsx tests/tests.arm9 +tests/tests.arm9 +tests/tests.nds tests/tests.nds tests/tests.nro +tests/tests.nro +tests/tests.rpx tests/tests.rpx utils/objfw-config utils/objfw-new/objfw-new utils/ofarc/ofarc utils/ofdns/ofdns utils/ofhash/ofhash utils/ofhttp/ofhttp Index: Makefile ================================================================== --- Makefile +++ Makefile @@ -15,11 +15,11 @@ .PHONY: check docs release utils tests: src check: tests - cd tests && ${MAKE} -s run + ${MAKE} -C tests -s run docs: rm -fr docs doxygen >/dev/null Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -737,11 +737,10 @@ __weak id foo = [Foo alloc]; (void)foo; ]]) ], [ AC_MSG_RESULT(yes) - AC_DEFINE(COMPILER_SUPPORTS_ARC, 1, [Whether the compiler supports ARC]) AC_SUBST(RUNTIME_ARC_TESTS_M, RuntimeARCTests.m) ], [ AC_MSG_RESULT(no) ]) OBJCFLAGS="$old_OBJCFLAGS" @@ -1976,11 +1975,11 @@ break ]) ]) ]) AS_IF([test x"$have_subprocesses" = x"yes"], [ - AC_SUBST(USE_SRCS_SUBPROCESS, '${SRCS_SUBPROCESS}') + AC_SUBST(USE_SRCS_SUBPROCESSES, '${SRCS_SUBPROCESSES}') AC_SUBST(SUBPROCESS, "subprocess") AC_DEFINE(OF_HAVE_SUBPROCESSES, 1, [Whether we have subprocesses]) ]) AC_CHECK_HEADERS_ONCE([complex.h sys/ioctl.h sys/ttycom.h]) Index: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -84,11 +84,11 @@ USE_SRCS_APPLETALK = @USE_SRCS_APPLETALK@ USE_SRCS_FILES = @USE_SRCS_FILES@ USE_SRCS_IPX = @USE_SRCS_IPX@ USE_SRCS_PLUGINS = @USE_SRCS_PLUGINS@ USE_SRCS_SOCKETS = @USE_SRCS_SOCKETS@ -USE_SRCS_SUBPROCESS = @USE_SRCS_SUBPROCESS@ +USE_SRCS_SUBPROCESSES = @USE_SRCS_SUBPROCESSES@ USE_SRCS_TAGGED_POINTERS = @USE_SRCS_TAGGED_POINTERS@ USE_SRCS_THREADS = @USE_SRCS_THREADS@ USE_SRCS_UNIX_SOCKETS = @USE_SRCS_UNIX_SOCKETS@ USE_SRCS_WINDOWS = @USE_SRCS_WINDOWS@ WRAPPER = @WRAPPER@ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -109,11 +109,11 @@ OFZIPArchive.m \ OFZIPArchiveEntry.m \ ${USE_SRCS_FILES} \ ${USE_SRCS_PLUGINS} \ ${USE_SRCS_SOCKETS} \ - ${USE_SRCS_SUBPROCESS} \ + ${USE_SRCS_SUBPROCESSES} \ ${USE_SRCS_THREADS} \ ${USE_SRCS_WINDOWS} SRCS_FILES = OFFile.m \ OFString+PathAdditions.m SRCS_PLUGINS = OFPlugin.m @@ -155,11 +155,11 @@ SRCS_IPX = OFIPXSocket.m \ OFSPXSocket.m \ OFSPXStreamSocket.m SRCS_UNIX_SOCKETS = OFUNIXDatagramSocket.m \ OFUNIXStreamSocket.m -SRCS_SUBPROCESS = OFSubprocess.m +SRCS_SUBPROCESSES = OFSubprocess.m SRCS_THREADS = OFCondition.m \ OFMutex.m \ OFPlainCondition.m \ OFPlainMutex.m \ OFPlainThread.m \ Index: src/OFStdIOStream.m ================================================================== --- src/OFStdIOStream.m +++ src/OFStdIOStream.m @@ -117,11 +117,10 @@ date.microsecond / 1000, me, getpid(), msg]; objc_autoreleasePoolPop(pool); } -#if defined(HAVE_ISATTY) && !defined(OF_WII_U) static int colorToANSI(OFColor *color) { if ([color isEqual: [OFColor black]]) return 30; @@ -156,11 +155,10 @@ if ([color isEqual: [OFColor white]]) return 97; return -1; } -#endif @implementation OFStdIOStream #ifndef OF_WINDOWS + (void)load { @@ -430,11 +428,14 @@ return OFMaxRetainCount; } - (bool)hasTerminal { -#if defined(HAVE_ISATTY) && !defined(OF_WII_U) +#if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) || \ + defined(OF_NINTENDO_SWITCH) + return true; +#elif defined(HAVE_ISATTY) && !defined(OF_WII_U) return isatty(_fd); #else return false; #endif } @@ -469,96 +470,81 @@ #endif } - (void)setForegroundColor: (OFColor *)color { -#if defined(HAVE_ISATTY) && !defined(OF_WII_U) int code; - if (!isatty(_fd)) + if (!self.hasTerminal) return; if ((code = colorToANSI(color)) == -1) return; [self writeFormat: @"\033[%um", code]; -#endif } - (void)setBackgroundColor: (OFColor *)color { -#if defined(HAVE_ISATTY) && !defined(OF_WII_U) int code; - if (!isatty(_fd)) + if (!self.hasTerminal) return; if ((code = colorToANSI(color)) == -1) return; [self writeFormat: @"\033[%um", code + 10]; -#endif } - (void)reset { -#if defined(HAVE_ISATTY) && !defined(OF_WII_U) - if (!isatty(_fd)) + if (!self.hasTerminal) return; [self writeString: @"\033[0m"]; -#endif } - (void)clear { -#if defined(HAVE_ISATTY) && !defined(OF_WII_U) - if (!isatty(_fd)) + if (!self.hasTerminal) return; [self writeString: @"\033[2J"]; -#endif } - (void)eraseLine { -#if defined(HAVE_ISATTY) && !defined(OF_WII_U) - if (!isatty(_fd)) + if (!self.hasTerminal) return; [self writeString: @"\033[2K"]; -#endif } - (void)setCursorColumn: (unsigned int)column { -#if defined(HAVE_ISATTY) && !defined(OF_WII_U) - if (!isatty(_fd)) + if (!self.hasTerminal) return; [self writeFormat: @"\033[%uG", column + 1]; -#endif } - (void)setCursorPosition: (OFPoint)position { if (position.x < 0 || position.y < 0) @throw [OFInvalidArgumentException exception]; -#if defined(HAVE_ISATTY) && !defined(OF_WII_U) - if (!isatty(_fd)) + if (!self.hasTerminal) return; [self writeFormat: @"\033[%u;%uH", (unsigned)position.y + 1, (unsigned)position.x + 1]; -#endif } - (void)setRelativeCursorPosition: (OFPoint)position { -#if defined(HAVE_ISATTY) && !defined(OF_WII_U) - if (!isatty(_fd)) + if (!self.hasTerminal) return; if (position.x > 0) [self writeFormat: @"\033[%uC", (unsigned)position.x]; else if (position.x < 0) @@ -566,8 +552,7 @@ if (position.y > 0) [self writeFormat: @"\033[%uB", (unsigned)position.y]; else if (position.y < 0) [self writeFormat: @"\033[%uA", (unsigned)-position.y]; -#endif } @end Index: src/OFString+PathAdditions.m ================================================================== --- src/OFString+PathAdditions.m +++ src/OFString+PathAdditions.m @@ -19,11 +19,11 @@ #if defined(OF_WINDOWS) || defined(OF_MSDOS) || defined(OF_MINT) # import "platform/Windows/OFString+PathAdditions.m" #elif defined(OF_AMIGAOS) # import "platform/AmigaOS/OFString+PathAdditions.m" -#elif defined(OF_NINTENDO_3DS) || defined(OF_WII) || \ - defined(OF_NINTENDO_SWITCH) +#elif defined(OF_WII) || defined(OF_NINTENDO_DS) || \ + defined(OF_NINTENDO_3DS) || defined(OF_NINTENDO_SWITCH) # import "platform/libfat/OFString+PathAdditions.m" #else # import "platform/POSIX/OFString+PathAdditions.m" #endif Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -2339,15 +2339,18 @@ #if defined(HAVE_STRTOF_L) || defined(HAVE_USELOCALE) const char *UTF8String = self.UTF8String; #else /* - * If we have no strtof_l, we have no other choice but to replace "." - * with the locale's decimal point. + * If we have no strtof_l, we have no other choice than to replace the + * locale's decimal point with something that will be rejected and + * replacing "." with the locale's decimal point. */ OFString *decimalSeparator = [OFLocale decimalSeparator]; - const char *UTF8String = [self + const char *UTF8String = [[self + stringByReplacingOccurrencesOfString: decimalSeparator + withString: @"!"] stringByReplacingOccurrencesOfString: @"." withString: decimalSeparator].UTF8String; #endif char *endPtr = NULL; float value; @@ -2396,15 +2399,18 @@ #if defined(HAVE_STRTOD_L) || defined(HAVE_USELOCALE) const char *UTF8String = self.UTF8String; #else /* - * If we have no strtod_l, we have no other choice but to replace "." - * with the locale's decimal point. + * If we have no strtod_l, we have no other choice than to replace the + * locale's decimal point with something that will be rejected and + * replacing "." with the locale's decimal point. */ OFString *decimalSeparator = [OFLocale decimalSeparator]; - const char *UTF8String = [self + const char *UTF8String = [[self + stringByReplacingOccurrencesOfString: decimalSeparator + withString: @"!"] stringByReplacingOccurrencesOfString: @"." withString: decimalSeparator].UTF8String; #endif char *endPtr = NULL; double value; Index: src/test/Makefile ================================================================== --- src/test/Makefile +++ src/test/Makefile @@ -2,16 +2,18 @@ DISTCLEAN = Info.plist STATIC_LIB = libobjfwtest.a -SRCS = OTAssert.m \ +SRCS = OTAssert.m \ + OTOrderedDictionary.m \ OTTestCase.m INCLUDES := ${SRCS:.m=.h} \ ObjFWTest.h SRCS += OTAppDelegate.m \ - OTAssertionFailedException.m + OTAssertionFailedException.m \ + OTTestSkippedException.m includesubdir = ObjFWTest include ../../buildsys.mk DELETED src/test/OTAppDelegate.h Index: src/test/OTAppDelegate.h ================================================================== --- src/test/OTAppDelegate.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 Index: src/test/OTAppDelegate.m ================================================================== --- src/test/OTAppDelegate.m +++ src/test/OTAppDelegate.m @@ -13,20 +13,74 @@ * file. */ #include "config.h" -#import "OTAppDelegate.h" - +#import "OFApplication.h" #import "OFColor.h" +#import "OFDictionary.h" +#import "OFMethodSignature.h" #import "OFSet.h" #import "OFStdIOStream.h" #import "OFValue.h" #import "OTTestCase.h" #import "OTAssertionFailedException.h" +#import "OTTestSkippedException.h" + +#ifdef OF_IOS +# include +#endif + +#ifdef OF_WII +# define asm __asm__ +# include +# include +# undef asm +#endif + +#ifdef OF_NINTENDO_DS +# define asm __asm__ +# include +# undef asm +#endif + +#ifdef OF_NINTENDO_3DS +/* Newer versions of libctru started using id as a parameter name. */ +# define id id_3ds +# include <3ds.h> +# undef id +#endif + +#ifdef OF_NINTENDO_SWITCH +# define id nx_id +# include +# undef id + +static OFDate *lastConsoleUpdate; + +static void +updateConsole(bool force) +{ + if (force || lastConsoleUpdate.timeIntervalSinceNow <= -1.0 / 60) { + consoleUpdate(NULL); + [lastConsoleUpdate release]; + lastConsoleUpdate = [[OFDate alloc] init]; + } +} +#endif + +@interface OTAppDelegate: OFObject +@end + +enum Status { + StatusRunning, + StatusOk, + StatusFailed, + StatusSkipped +}; OF_APPLICATION_DELEGATE(OTAppDelegate) static bool isSubclassOfClass(Class class, Class superclass) @@ -37,10 +91,64 @@ return false; } @implementation OTAppDelegate ++ (void)initialize +{ + if (self != [OTAppDelegate class]) + return; + +#if defined(OF_IOS) + CFBundleRef mainBundle = CFBundleGetMainBundle(); + CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); + UInt8 resourcesPath[PATH_MAX]; + + if (!CFURLGetFileSystemRepresentation(resourcesURL, true, resourcesPath, + PATH_MAX)) { + [OFStdErr writeLine: @"Failed to locate resources!"]; + [OFApplication terminateWithStatus: 1]; + } + + [[OFFileManager defaultManager] changeCurrentDirectoryPath: + [OFString stringWithUTF8String: (const char *)resourcesPath]]; + + CFRelease(resourcesURL); +#elif defined(OF_WII) + GXRModeObj *mode; + void *nextFB; + + VIDEO_Init(); + WPAD_Init(); + + mode = VIDEO_GetPreferredMode(NULL); + nextFB = MEM_K0_TO_K1(SYS_AllocateFramebuffer(mode)); + VIDEO_Configure(mode); + VIDEO_SetNextFramebuffer(nextFB); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + + VIDEO_WaitVSync(); + if (mode->viTVMode & VI_NON_INTERLACE) + VIDEO_WaitVSync(); + + CON_InitEx(mode, 2, 2, mode->fbWidth - 4, mode->xfbHeight - 4); + VIDEO_ClearFrameBuffer(mode, nextFB, COLOR_BLACK); +#elif defined(OF_NINTENDO_DS) + consoleDemoInit(); +#elif defined(OF_NINTENDO_3DS) + gfxInitDefault(); + atexit(gfxExit); + + consoleInit(GFX_TOP, NULL); +#elif defined(OF_NINTENDO_SWITCH) + consoleInit(NULL); + padConfigureInput(1, HidNpadStyleSet_NpadStandard); + updateConsole(true); +#endif +} + - (OFSet OF_GENERIC(Class) *)testClasses { Class *classes = objc_copyClassList(NULL); OFMutableSet *testClasses; @@ -54,11 +162,13 @@ /* * Make sure the class is initialized. * Required for the ObjFW runtime, as otherwise * class_getSuperclass() crashes. */ +#ifdef OF_OBJFW_RUNTIME [*iter class]; +#endif /* * Don't use +[isSubclassOfClass:], as the Apple runtime * can return (presumably internal?) classes that don't * implement it, resulting in a crash. @@ -87,85 +197,238 @@ @try { tests = [OFMutableSet set]; for (Method *iter = methods; *iter != NULL; iter++) { SEL selector = method_getName(*iter); + void *pool; + OFMethodSignature *sig; if (selector == NULL) continue; - if (strncmp(sel_getName(selector), "test", 4) == 0) + if (strncmp(sel_getName(selector), "test", 4) != 0) + continue; + + pool = objc_autoreleasePoolPush(); + sig = [OFMethodSignature signatureWithObjCTypes: + method_getTypeEncoding(*iter)]; + + if (strcmp(sig.methodReturnType, "v") == 0 && + sig.numberOfArguments == 2 && + strcmp([sig argumentTypeAtIndex: 0], "@") == 0 && + strcmp([sig argumentTypeAtIndex: 1], ":") == 0) [tests addObject: [OFValue valueWithPointer: selector]]; + + objc_autoreleasePoolPop(pool); } } @finally { OFFreeMemory(methods); } + if (class_getSuperclass(class) != Nil) + [tests unionSet: + [self testsInClass: class_getSuperclass(class)]]; + [tests makeImmutable]; return tests; } - (void)printStatusForTest: (SEL)test inClass: (Class)class - status: (int)status + status: (enum Status)status description: (OFString *)description { switch (status) { - case 0: - [OFStdOut setForegroundColor: [OFColor olive]]; - [OFStdOut writeFormat: @"-[%@ ", class]; - [OFStdOut setForegroundColor: [OFColor yellow]]; - [OFStdOut writeFormat: @"%s", sel_getName(test)]; - [OFStdOut setForegroundColor: [OFColor olive]]; - [OFStdOut writeString: @"]: "]; - break; - case 1: - [OFStdOut setForegroundColor: [OFColor green]]; - [OFStdOut writeFormat: @"\r-[%@ ", class]; - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeFormat: @"%s", sel_getName(test)]; - [OFStdOut setForegroundColor: [OFColor green]]; - [OFStdOut writeLine: @"]: ok"]; - break; - case 2: - [OFStdOut setForegroundColor: [OFColor maroon]]; - [OFStdOut writeFormat: @"\r-[%@ ", class]; - [OFStdOut setForegroundColor: [OFColor red]]; - [OFStdOut writeFormat: @"%s", sel_getName(test)]; - [OFStdOut setForegroundColor: [OFColor maroon]]; - [OFStdOut writeLine: @"]: failed"]; - [OFStdOut writeLine: description]; - break; - } - - [OFStdOut reset]; + case StatusRunning: + if (OFStdOut.hasTerminal) { + [OFStdOut setForegroundColor: [OFColor olive]]; + [OFStdOut writeFormat: @"-[%@ ", class]; + [OFStdOut setForegroundColor: [OFColor yellow]]; + [OFStdOut writeFormat: @"%s", sel_getName(test)]; + [OFStdOut setForegroundColor: [OFColor olive]]; + [OFStdOut writeString: @"]: "]; + } else + [OFStdOut writeFormat: @"-[%@ %s]: ", + class, sel_getName(test)]; + break; + case StatusOk: + if (OFStdOut.hasTerminal) { + [OFStdOut setForegroundColor: [OFColor green]]; + [OFStdOut writeFormat: @"\r-[%@ ", class]; + [OFStdOut setForegroundColor: [OFColor lime]]; + [OFStdOut writeFormat: @"%s", sel_getName(test)]; + [OFStdOut setForegroundColor: [OFColor green]]; + [OFStdOut writeLine: @"]: ok"]; + } else + [OFStdOut writeLine: @"ok"]; + break; + case StatusFailed: + if (OFStdOut.hasTerminal) { + [OFStdOut setForegroundColor: [OFColor maroon]]; + [OFStdOut writeFormat: @"\r-[%@ ", class]; + [OFStdOut setForegroundColor: [OFColor red]]; + [OFStdOut writeFormat: @"%s", sel_getName(test)]; + [OFStdOut setForegroundColor: [OFColor maroon]]; + [OFStdOut writeLine: @"]: failed"]; + [OFStdOut writeLine: description]; + } else + [OFStdOut writeLine: @"failed"]; + break; + case StatusSkipped: + if (OFStdOut.hasTerminal) { + [OFStdOut setForegroundColor: [OFColor gray]]; + [OFStdOut writeFormat: @"\r-[%@ ", class]; + [OFStdOut setForegroundColor: [OFColor silver]]; + [OFStdOut writeFormat: @"%s", sel_getName(test)]; + [OFStdOut setForegroundColor: [OFColor gray]]; + [OFStdOut writeLine: @"]: skipped"]; + } else + [OFStdOut writeLine: @"skipped"]; + + if (description != nil) + [OFStdOut writeLine: description]; + + break; + } + + if (status == StatusFailed) { +#if defined(OF_WII) + [OFStdOut setForegroundColor: [OFColor silver]]; + [OFStdOut writeLine: @"Press A to continue"]; + + for (;;) { + WPAD_ScanPads(); + + if (WPAD_ButtonsDown(0) & WPAD_BUTTON_A) + break; + + VIDEO_WaitVSync(); + } +#elif defined(OF_NINTENDO_DS) + [OFStdOut setForegroundColor: [OFColor silver]]; + [OFStdOut writeLine: @"Press A to continue"]; + + for (;;) { + swiWaitForVBlank(); + scanKeys(); + + if (keysDown() & KEY_A) + break; + } +#elif defined(OF_NINTENDO_3DS) + [OFStdOut setForegroundColor: [OFColor silver]]; + [OFStdOut writeLine: @"Press A to continue"]; + + for (;;) { + hidScanInput(); + + if (hidKeysDown() & KEY_A) + break; + + gspWaitForVBlank(); + } +#elif defined(OF_NINTENDO_SWITCH) + [OFStdOut setForegroundColor: [OFColor silver]]; + [OFStdOut writeLine: @"Press A to continue"]; + + while (appletMainLoop()) { + PadState pad; + + padUpdate(&pad); + updateConsole(true); + + if (padGetButtonsDown(&pad) & HidNpadButton_A) + break; + } +#endif + } +} + +- (OFString *)descriptionForException: (id)exception +{ + OFMutableString *description = [OFMutableString + stringWithFormat: @"Unhandled exception: %@", + exception]; + OFArray OF_GENERIC(OFValue *) *stackTraceAddresses = nil; + OFArray OF_GENERIC(OFString *) *stackTraceSymbols = nil; + OFStringEncoding encoding = [OFLocale encoding]; + + if ([exception respondsToSelector: @selector(stackTraceAddresses)]) + stackTraceAddresses = [exception stackTraceAddresses]; + + if (stackTraceAddresses != nil) { + size_t count = stackTraceAddresses.count; + + if ([exception respondsToSelector: + @selector(stackTraceSymbols)]) + stackTraceSymbols = [exception stackTraceSymbols]; + + if (stackTraceSymbols.count != count) + stackTraceSymbols = nil; + + [description appendString: @"\n\nStack trace:"]; + + if (stackTraceSymbols != nil) { + for (size_t i = 0; i < count; i++) { + void *address = [[stackTraceAddresses + objectAtIndex: i] pointerValue]; + const char *symbol = [[stackTraceSymbols + objectAtIndex: i] + cStringWithEncoding: encoding]; + + [description appendFormat: @"\n %p %s", + address, symbol]; + } + } else { + for (size_t i = 0; i < count; i++) { + void *address = [[stackTraceAddresses + objectAtIndex: i] pointerValue]; + + [description appendFormat: @"\n %p", address]; + } + } + } + + [description makeImmutable]; + + return description; } - (void)applicationDidFinishLaunching: (OFNotification *)notification { OFSet OF_GENERIC(Class) *testClasses = [self testClasses]; - size_t numSucceeded = 0, numFailed = 0; + size_t numSucceeded = 0, numFailed = 0, numSkipped = 0; + OFMutableDictionary *summaries = [OFMutableDictionary dictionary]; - [OFStdOut writeFormat: @"Running %zu test case(s)\n", - testClasses.count]; + [OFStdOut setForegroundColor: [OFColor purple]]; + [OFStdOut writeString: @"Found "]; +#if !defined(OF_WII) && !defined(OF_NINTENDO_DS) && \ + !defined(OF_NINTENDO_3DS) && !defined(OF_NINTENDO_SWITCH) + [OFStdOut setForegroundColor: [OFColor fuchsia]]; +#endif + [OFStdOut writeFormat: @"%zu", testClasses.count]; + [OFStdOut setForegroundColor: [OFColor purple]]; + [OFStdOut writeFormat: @" test case%s\n", + (testClasses.count != 1 ? "s" : "")]; for (Class class in testClasses) { + OFArray *summary; + [OFStdOut setForegroundColor: [OFColor teal]]; [OFStdOut writeFormat: @"Running ", class]; [OFStdOut setForegroundColor: [OFColor aqua]]; [OFStdOut writeFormat: @"%@\n", class]; - [OFStdOut reset]; for (OFValue *test in [self testsInClass: class]) { void *pool = objc_autoreleasePoolPush(); - bool failed = false; + bool failed = false, skipped = false; OTTestCase *instance; [self printStatusForTest: test.pointerValue inClass: class - status: 0 + status: StatusRunning description: nil]; instance = [[[class alloc] init] autorelease]; @try { @@ -178,12 +441,30 @@ * If an assertion fails during a test, abort * the test. */ [self printStatusForTest: test.pointerValue inClass: class - status: 2 + status: StatusFailed + description: e.description]; + + failed = true; + } @catch (OTTestSkippedException *e) { + [self printStatusForTest: test.pointerValue + inClass: class + status: StatusSkipped description: e.description]; + + skipped = true; + } @catch (id e) { + OFString *description = + [self descriptionForException: e]; + + [self printStatusForTest: test.pointerValue + inClass: class + status: StatusFailed + description: description]; + failed = true; } @try { [instance tearDown]; } @catch (OTAssertionFailedException *e) { @@ -195,37 +476,131 @@ SEL selector = test.pointerValue; OFString *description = e.description; [self printStatusForTest: selector inClass: class - status: 2 + status: StatusFailed description: description]; + failed = true; } + } @catch (id e) { + OFString *description = + [self descriptionForException: e]; + + [self printStatusForTest: test.pointerValue + inClass: class + status: StatusFailed + description: description]; + + failed = true; } - if (!failed) { + if (!failed && !skipped) { [self printStatusForTest: test.pointerValue inClass: class - status: 1 + status: StatusOk description: nil]; + numSucceeded++; - } else + } else if (failed) numFailed++; + else if (skipped) + numSkipped++; objc_autoreleasePoolPop(pool); } + + summary = [class summary]; + if (summary != nil) + [summaries setObject: summary forKey: class]; + } + + for (Class class in summaries) { + OFArray *summary = [summaries objectForKey: class]; + + [OFStdOut setForegroundColor: [OFColor teal]]; + [OFStdOut writeString: @"Summary for "]; + [OFStdOut setForegroundColor: [OFColor aqua]]; + [OFStdOut writeFormat: @"%@\n", class]; + + for (OFPair *line in summary) { + [OFStdOut setForegroundColor: [OFColor navy]]; + [OFStdOut writeFormat: @"%@: ", line.firstObject]; + [OFStdOut setForegroundColor: [OFColor blue]]; + [OFStdOut writeFormat: @"%@\n", line.secondObject]; + } } +#if !defined(OF_WII) && !defined(OF_NINTENDO_DS) && \ + !defined(OF_NINTENDO_3DS) && !defined(OF_NINTENDO_SWITCH) [OFStdOut setForegroundColor: [OFColor fuchsia]]; +#else + [OFStdOut setForegroundColor: [OFColor purple]]; +#endif [OFStdOut writeFormat: @"%zu", numSucceeded]; [OFStdOut setForegroundColor: [OFColor purple]]; - [OFStdOut writeString: @" test(s) succeeded, "]; + [OFStdOut writeFormat: @" test%s succeeded, ", + (numSucceeded != 1 ? "s" : "")]; +#if !defined(OF_WII) && !defined(OF_NINTENDO_DS) && \ + !defined(OF_NINTENDO_3DS) && !defined(OF_NINTENDO_SWITCH) [OFStdOut setForegroundColor: [OFColor fuchsia]]; +#endif [OFStdOut writeFormat: @"%zu", numFailed]; [OFStdOut setForegroundColor: [OFColor purple]]; - [OFStdOut writeLine: @" test(s) failed."]; + [OFStdOut writeFormat: @" test%s failed, ", + (numFailed != 1 ? "s" : "")]; +#if !defined(OF_WII) && !defined(OF_NINTENDO_DS) && \ + !defined(OF_NINTENDO_3DS) && !defined(OF_NINTENDO_SWITCH) + [OFStdOut setForegroundColor: [OFColor fuchsia]]; +#endif + [OFStdOut writeFormat: @"%zu", numSkipped]; + [OFStdOut setForegroundColor: [OFColor purple]]; + [OFStdOut writeFormat: @" test%s skipped\n", + (numSkipped != 1 ? "s" : "")]; [OFStdOut reset]; + +#if defined(OF_WII) + [OFStdOut setForegroundColor: [OFColor silver]]; + [OFStdOut writeLine: @"Press home button to exit"]; + + for (;;) { + WPAD_ScanPads(); + + if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) + break; + + VIDEO_WaitVSync(); + } +#elif defined(OF_NINTENDO_DS) + [OFStdOut setForegroundColor: [OFColor silver]]; + [OFStdOut writeLine: @"Press start button to exit"]; + + for (;;) { + swiWaitForVBlank(); + scanKeys(); + + if (keysDown() & KEY_START) + break; + } +#elif defined(OF_NINTENDO_3DS) + [OFStdOut setForegroundColor: [OFColor silver]]; + [OFStdOut writeLine: @"Press start button to exit"]; + + for (;;) { + hidScanInput(); + + if (hidKeysDown() & KEY_START) + break; + + gspWaitForVBlank(); + } +#elif defined(OF_NINTENDO_SWITCH) + while (appletMainLoop()) + updateConsole(true); + + consoleExit(NULL); +#endif [OFApplication terminateWithStatus: (int)numFailed]; } @end Index: src/test/OTAssert.h ================================================================== --- src/test/OTAssert.h +++ src/test/OTAssert.h @@ -24,10 +24,14 @@ ## __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 OTAssertLessThan(a, b, ...) OTAssert(a < b, ## __VA_ARGS__) +#define OTAssertLessThanOrEqual(a, b, ...) OTAssert(a <= b, ## __VA_ARGS__) +#define OTAssertGreaterThan(a, b, ...) OTAssert(a > b, ## __VA_ARGS__) +#define OTAssertGreaterThanOrEqual(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__) #define OTAssertNil(object, ...) OTAssert(object == nil, ## __VA_ARGS__) #define OTAssertNotNil(object, ...) OTAssert(object != nil, ## __VA_ARGS__) @@ -49,14 +53,17 @@ } @catch (exception *e) { \ OTThrown = true; \ } \ OTAssert(OTThrown, ## __VA_ARGS__); \ } +#define OTSkip(...) \ + OTSkipImpl(self, _cmd, @__FILE__, __LINE__, ## __VA_ARGS__, nil) #ifdef __cplusplus extern "C" { #endif extern void OTAssertImpl(id testCase, SEL test, bool condition, OFString *check, OFString *file, size_t line, ...); +extern void OTSkipImpl(id testCase, SEL test, OFString *file, size_t line, ...); #ifdef __cplusplus } #endif Index: src/test/OTAssert.m ================================================================== --- src/test/OTAssert.m +++ src/test/OTAssert.m @@ -16,10 +16,11 @@ #include "config.h" #import "OFString.h" #import "OTAssertionFailedException.h" +#import "OTTestSkippedException.h" void OTAssertImpl(id testCase, SEL test, bool condition, OFString *check, OFString *file, size_t line, ...) { @@ -41,5 +42,25 @@ va_end(arguments); @throw [OTAssertionFailedException exceptionWithCondition: check message: message]; } + +void +OTSkipImpl(id testCase, SEL test, OFString *file, size_t line, ...) +{ + va_list arguments; + OFConstantString *format; + OFString *message = nil; + + va_start(arguments, line); + format = va_arg(arguments, OFConstantString *); + + if (format != nil) + message = [[[OFString alloc] + initWithFormat: format + arguments: arguments] autorelease]; + + va_end(arguments); + + @throw [OTTestSkippedException exceptionWithMessage: message]; +} ADDED src/test/OTOrderedDictionary.h Index: src/test/OTOrderedDictionary.h ================================================================== --- /dev/null +++ src/test/OTOrderedDictionary.h @@ -0,0 +1,31 @@ +/* + * 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 "ObjFW.h" +#else +# import +#endif + +OF_ASSUME_NONNULL_BEGIN + +@interface OTOrderedDictionary: OFDictionary +{ + OFArray *_keys; + OFArray *_objects; +} +@end + +OF_ASSUME_NONNULL_END ADDED src/test/OTOrderedDictionary.m Index: src/test/OTOrderedDictionary.m ================================================================== --- /dev/null +++ src/test/OTOrderedDictionary.m @@ -0,0 +1,88 @@ +/* + * 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 "OTOrderedDictionary.h" + +@implementation OTOrderedDictionary +- (instancetype)initWithObjects: (id const *)objects + forKeys: (id const *)keys + count: (size_t)count +{ + self = [super init]; + + @try { + OFMutableArray *mutableKeys, *mutableObjects; + + mutableKeys = [[OFMutableArray alloc] initWithCapacity: count]; + _keys = mutableKeys; + + mutableObjects = [[OFMutableArray alloc] + initWithCapacity: count]; + _objects = mutableObjects; + + for (size_t i = 0; i < count; i++) { + [mutableKeys addObject: keys[i]]; + [mutableObjects addObject: objects[i]]; + } + + [mutableKeys makeImmutable]; + [mutableObjects makeImmutable]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_keys release]; + [_objects release]; + + [super dealloc]; +} + +- (id)objectForKey: (id)key +{ + size_t i = 0; + + for (id iter in _keys) { + if ([iter isEqual: key]) + return [_objects objectAtIndex: i]; + + i++; + } + + return nil; +} + +- (size_t)count +{ + return _keys.count; +} + +- (OFEnumerator *)keyEnumerator +{ + return [_keys objectEnumerator]; +} + +- (OFEnumerator *)objectEnumerator +{ + return [_objects objectEnumerator]; +} +@end Index: src/test/OTTestCase.h ================================================================== --- src/test/OTTestCase.h +++ src/test/OTTestCase.h @@ -12,18 +12,24 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #ifdef OBJFWTEST_LOCAL_INCLUDES -# import "OFObject.h" +# import "ObjFW.h" #else -# import +# import #endif OF_ASSUME_NONNULL_BEGIN @interface OTTestCase: OFObject +#ifdef OF_HAVE_CLASS_PROPERTIES +@property (class, readonly, nullable, nonatomic) + OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, id) *) *summary; +#endif + ++ (nullable OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, id) *) *)summary; - (void)setUp; - (void)tearDown; @end OF_ASSUME_NONNULL_END Index: src/test/OTTestCase.m ================================================================== --- src/test/OTTestCase.m +++ src/test/OTTestCase.m @@ -16,10 +16,15 @@ #include "config.h" #import "OTTestCase.h" @implementation OTTestCase: OFObject ++ (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, id) *) *)summary +{ + return nil; +} + - (void)setUp { } - (void)tearDown ADDED src/test/OTTestSkippedException.h Index: src/test/OTTestSkippedException.h ================================================================== --- /dev/null +++ src/test/OTTestSkippedException.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. + */ + +#import "OFException.h" +#import "OFString.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OTTestSkippedException: OFException +{ + OFString *_Nullable _message; +} + +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *message; + ++ (instancetype)exceptionWithMessage: (nullable OFString *)message; ++ (instancetype)exception OF_UNAVAILABLE; +- (instancetype)initWithMessage: (nullable OFString *)message; +- (instancetype)init OF_UNAVAILABLE; +@end + +OF_ASSUME_NONNULL_END ADDED src/test/OTTestSkippedException.m Index: src/test/OTTestSkippedException.m ================================================================== --- /dev/null +++ src/test/OTTestSkippedException.m @@ -0,0 +1,67 @@ +/* + * 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 "OTTestSkippedException.h" + +@implementation OTTestSkippedException +@synthesize message = _message; + ++ (instancetype)exceptionWithMessage: (OFString *)message +{ + return [[[self alloc] initWithMessage: message] autorelease]; +} + ++ (instancetype)exception +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (instancetype)initWithMessage: (OFString *)message +{ + self = [super init]; + + @try { + _message = [message copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (void)dealloc +{ + [_message release]; + + [super dealloc]; +} + +- (OFString *)description +{ + if (_message != nil) + return [OFString stringWithFormat: @"Test skipped: %@", + _message]; + else + return nil; +} +@end Index: src/test/ObjFWTest.h ================================================================== --- src/test/ObjFWTest.h +++ src/test/ObjFWTest.h @@ -13,5 +13,6 @@ * file. */ #import "OTTestCase.h" #import "OTAssert.h" +#import "OTOrderedDictionary.h" Index: tests/ForwardingTests.m ================================================================== --- tests/ForwardingTests.m +++ tests/ForwardingTests.m @@ -15,30 +15,33 @@ #include "config.h" #include -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" #define FORMAT @"%@ %@ %@ %@ %@ %@ %@ %@ %@ %g %g %g %g %g %g %g %g %g" #define ARGS @"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h", @"i", \ 1.5, 2.25, 3.125, 4.0625, 5.03125, 6.5, 7.25, 8.0, 9.0 #define RESULT @"a b c d e f g h i 1.5 2.25 3.125 4.0625 5.03125 6.5 7.25 8 9" -static OFString *const module = @"Forwarding"; +@interface ForwardingTests: OTTestCase +@end + static size_t forwardingsCount = 0; static bool success = false; static id target = nil; struct StretTest { char buffer[1024]; }; -@interface ForwardingTest: OFObject +@interface ForwardingTestObject: OFObject @end -@interface ForwardingTest (Test) +@interface ForwardingTestObject (NonExistentMethods) + (void)test; - (void)test; - (uint32_t)forwardingTargetTest: (intptr_t)a0 : (intptr_t)a1 : (double)a2 @@ -59,11 +62,149 @@ test(id self, SEL _cmd) { success = true; } -@implementation ForwardingTest +@implementation ForwardingTests +- (void)setUp +{ + [super setUp]; + + forwardingsCount = 0; + success = false; + target = nil; +} + +- (void)testForwardingMessageAndAddingClassMethod +{ + [ForwardingTestObject test]; + OTAssertTrue(success); + OTAssertEqual(forwardingsCount, 1); + + [ForwardingTestObject test]; + OTAssertEqual(forwardingsCount, 1); +} + +- (void)forwardingMessageAndAddingInstanceMethod +{ + ForwardingTestObject *testObject = + [[[ForwardingTestObject alloc] init] autorelease]; + + [testObject test]; + OTAssertTrue(success); + OTAssertEqual(forwardingsCount, 1); + + [testObject test]; + OTAssertEqual(forwardingsCount, 1); +} + +#ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR +- (void)testForwardingTargetForSelector +{ + ForwardingTestObject *testObject = + [[[ForwardingTestObject alloc] init] autorelease]; + + target = [[[ForwardingTarget alloc] init] autorelease]; + + OTAssertEqual( + [testObject forwardingTargetTest: 0xDEADBEEF + : -1 + : 1.25 + : 2.75], 0x12345678); +} + +- (void)testForwardingTargetForSelectorWithVariableArguments +{ + ForwardingTestObject *testObject = + [[[ForwardingTestObject alloc] init] autorelease]; + + target = [[[ForwardingTarget alloc] init] autorelease]; + + OTAssertEqualObjects( + ([testObject forwardingTargetVarArgTest: FORMAT, ARGS]), RESULT); +} + +/* + * Don't try fpret on Win64 if we don't have stret forwarding, as long double + * is handled as a struct there. + */ +# if !defined(OF_WINDOWS) || !defined(OF_AMD64) || \ + defined(OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET) +- (void)testForwardingTargetForSelectorFPRet +{ + ForwardingTestObject *testObject = + [[[ForwardingTestObject alloc] init] autorelease]; + + target = [[[ForwardingTarget alloc] init] autorelease]; + + OTAssertEqual([testObject forwardingTargetFPRetTest], + 12345678.00006103515625); +} +# endif + +# ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET +- (void)testForwardingTargetForSelectorStRet +{ + ForwardingTestObject *testObject = + [[[ForwardingTestObject alloc] init] autorelease]; + + target = [[[ForwardingTarget alloc] init] autorelease]; + + OTAssertEqual(memcmp([testObject forwardingTargetStRetTest].buffer, + "abcdefghijklmnopqrstuvwxyz", 27), 0); +} +# endif + +- (void)testForwardingTargetForSelectorReturningNilThrows +{ + ForwardingTestObject *testObject = + [[[ForwardingTestObject alloc] init] autorelease]; + + target = [[[ForwardingTarget alloc] init] autorelease]; + + OTAssertThrowsSpecific([testObject forwardingTargetNilTest], + OFNotImplementedException); +} + +- (void)testForwardingTargetForSelectorReturningSelfThrows +{ + ForwardingTestObject *testObject = + [[[ForwardingTestObject alloc] init] autorelease]; + + target = [[[ForwardingTarget alloc] init] autorelease]; + + OTAssertThrowsSpecific([testObject forwardingTargetSelfTest], + OFNotImplementedException); +} + +# ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET +- (void)testForwardingTargetForSelectorStRetReturningNilThrows +{ + ForwardingTestObject *testObject = + [[[ForwardingTestObject alloc] init] autorelease]; + + target = [[[ForwardingTarget alloc] init] autorelease]; + + OTAssertThrowsSpecific([testObject forwardingTargetNilStRetTest], + OFNotImplementedException); +} + +- (void)testForwardingTargetForSelectorStRetReturningSelfThrows +{ + ForwardingTestObject *testObject = + [[[ForwardingTestObject alloc] init] autorelease]; + + target = [[[ForwardingTarget alloc] init] autorelease]; + + OTAssertThrowsSpecific([testObject forwardingTargetSelfStRetTest], + OFNotImplementedException); +} +# endif +#endif +@end + +@implementation ForwardingTestObject + (bool)resolveClassMethod: (SEL)selector { forwardingsCount++; if (sel_isEqual(selector, @selector(test))) { @@ -171,67 +312,6 @@ memcpy(ret.buffer, "abcdefghijklmnopqrstuvwxyz", 27); return ret; } -@end - -@implementation TestsAppDelegate (ForwardingTests) -- (void)forwardingTests -{ - void *pool = objc_autoreleasePoolPush(); - - TEST(@"Forwarding a message and adding a class method", - R([ForwardingTest test]) && success && - R([ForwardingTest test]) && forwardingsCount == 1); - - ForwardingTest *testObject = - [[[ForwardingTest alloc] init] autorelease]; - - success = false; - forwardingsCount = 0; - - TEST(@"Forwarding a message and adding an instance method", - R([testObject test]) && success && R([testObject test]) && - forwardingsCount == 1); - -#ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR - target = [[[ForwardingTarget alloc] init] autorelease]; - TEST(@"-[forwardingTargetForSelector:]", - [testObject forwardingTargetTest: 0xDEADBEEF - : -1 - : 1.25 - : 2.75] == 0x12345678) - TEST(@"-[forwardingTargetForSelector:] variable arguments", - [[testObject forwardingTargetVarArgTest: FORMAT, ARGS] - isEqual: RESULT]) - /* - * Don't try fpret on Win64 if we don't have stret forwarding, as - * long double is handled as a struct there. - */ -# if !defined(OF_WINDOWS) || !defined(OF_AMD64) || \ - defined(OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET) - TEST(@"-[forwardingTargetForSelector:] fp return", - [testObject forwardingTargetFPRetTest] == 12345678.00006103515625) -# endif -# ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET - TEST(@"-[forwardingTargetForSelector:] struct return", - !memcmp([testObject forwardingTargetStRetTest].buffer, - "abcdefghijklmnopqrstuvwxyz", 27)) -# endif - EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] nil target", - OFNotImplementedException, [testObject forwardingTargetNilTest]) - EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] self target", - OFNotImplementedException, [testObject forwardingTargetSelfTest]) -# ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET - EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] nil target + " - @"stret", OFNotImplementedException, - [testObject forwardingTargetNilStRetTest]) - EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] self target + " - @"stret", OFNotImplementedException, - [testObject forwardingTargetSelfStRetTest]) -# endif -#endif - - objc_autoreleasePoolPop(pool); -} @end Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -1,11 +1,8 @@ include ../extra.mk -SUBDIRS = ${TESTPLUGIN} \ - ${OBJC_SYNC} \ - ${SUBPROCESS} \ - terminal +SUBDIRS = ${TESTPLUGIN} ${SUBPROCESS} ${OBJC_SYNC} terminal CLEAN = EBOOT.PBP \ boot.dol \ ${PROG_NOINST}.arm9 \ ${PROG_NOINST}.nds \ @@ -15,85 +12,87 @@ testfile_ini.m DISTCLEAN = Info.plist PROG_NOINST = tests${PROG_SUFFIX} STATIC_LIB_NOINST = ${TESTS_STATIC_LIB} -SRCS = ForwardingTests.m \ - OFArrayTests.m \ - ${OF_BLOCK_TESTS_M} \ - OFCharacterSetTests.m \ - OFColorTests.m \ - OFDataTests.m \ - OFDateTests.m \ - OFDictionaryTests.m \ - OFHMACTests.m \ - OFINIFileTests.m \ - OFIRITests.m \ - OFInvocationTests.m \ - OFJSONTests.m \ - OFListTests.m \ - OFLocaleTests.m \ - OFMD5HashTests.m \ - OFMatrix4x4Tests.m \ - OFMemoryStreamTests.m \ - OFMethodSignatureTests.m \ - OFNotificationCenterTests.m \ - OFNumberTests.m \ - OFObjectTests.m \ - OFPBKDF2Tests.m \ - OFPropertyListTests.m \ - OFRIPEMD160HashTests.m \ - OFSHA1HashTests.m \ - OFSHA224HashTests.m \ - OFSHA256HashTests.m \ - OFSHA384HashTests.m \ - OFSHA512HashTests.m \ - OFScryptTests.m \ - OFSetTests.m \ - OFStreamTests.m \ - OFStringTests.m \ - OFSystemInfoTests.m \ - OFValueTests.m \ - OFXMLElementBuilderTests.m \ - OFXMLNodeTests.m \ - OFXMLParserTests.m \ - RuntimeTests.m \ - ${RUNTIME_ARC_TESTS_M} \ - TestsAppDelegate.m \ - ${USE_SRCS_FILES} \ - ${USE_SRCS_PLUGINS} \ - ${USE_SRCS_SOCKETS} \ - ${USE_SRCS_SUBPROCESS} \ - ${USE_SRCS_THREADS} \ - ${USE_SRCS_WINDOWS} \ - testfile_bin.m \ +SRCS = ${OF_BLOCK_TESTS_M} \ + ForwardingTests.m \ + OFArrayTests.m \ + OFCharacterSetTests.m \ + OFColorTests.m \ + OFConcreteArrayTests.m \ + OFConcreteDictionaryTests.m \ + OFConcreteMutableArrayTests.m \ + OFConcreteMutableDictionaryTests.m \ + OFConcreteMutableSetTests.m \ + OFConcreteSetTests.m \ + OFCryptographicHashTests.m \ + OFDataTests.m \ + OFDateTests.m \ + OFDictionaryTests.m \ + OFHMACTests.m \ + OFINIFileTests.m \ + OFIRITests.m \ + OFInvocationTests.m \ + OFJSONTests.m \ + OFListTests.m \ + OFLocaleTests.m \ + OFMatrix4x4Tests.m \ + OFMemoryStreamTests.m \ + OFMethodSignatureTests.m \ + OFMutableArrayTests.m \ + OFMutableDataTests.m \ + OFMutableDictionaryTests.m \ + OFMutableSetTests.m \ + OFMutableStringTests.m \ + OFMutableUTF8StringTests.m \ + OFNotificationCenterTests.m \ + OFNumberTests.m \ + OFObjectTests.m \ + OFPBKDF2Tests.m \ + OFPropertyListTests.m \ + OFScryptTests.m \ + OFSetTests.m \ + OFStreamTests.m \ + OFStringTests.m \ + OFSystemInfoTests.m \ + OFUTF8StringTests.m \ + OFValueTests.m \ + OFXMLElementBuilderTests.m \ + OFXMLNodeTests.m \ + OFXMLParserTests.m \ + ${RUNTIME_ARC_TESTS_M} \ + RuntimeTests.m \ + ${USE_SRCS_PLUGINS} \ + ${USE_SRCS_SOCKETS} \ + ${USE_SRCS_SUBPROCESSES} \ + ${USE_SRCS_THREADS} \ + ${USE_SRCS_WINDOWS} \ + testfile_bin.m \ testfile_ini.m SRCS_PLUGINS = OFPluginTests.m SRCS_SOCKETS = OFDNSResolverTests.m \ ${OF_HTTP_CLIENT_TESTS_M} \ - OFHTTPCookieTests.m \ OFHTTPCookieManagerTests.m \ + OFHTTPCookieTests.m \ OFKernelEventObserverTests.m \ OFSocketTests.m \ OFTCPSocketTests.m \ OFUDPSocketTests.m \ - ${USE_SRCS_APPLETALK} \ ${USE_SRCS_IPX} \ - ${USE_SRCS_UNIX_SOCKETS} + ${USE_SRCS_UNIX_SOCKETS} \ + ${USE_SRCS_APPLETALK} SRCS_APPLETALK = OFDDPSocketTests.m SRCS_IPX = OFIPXSocketTests.m \ OFSPXSocketTests.m \ OFSPXStreamSocketTests.m SRCS_UNIX_SOCKETS = OFUNIXDatagramSocketTests.m \ OFUNIXStreamSocketTests.m -SRCS_SUBPROCESS = OFSubprocessTests.m +SRCS_SUBPROCESSES = OFSubprocessTests.m SRCS_THREADS = OFThreadTests.m SRCS_WINDOWS = OFWindowsRegistryKeyTests.m -IOS_USER ?= mobile -IOS_TMP ?= /tmp/objfw-test - include ../buildsys.mk testfile_bin.m: testfile.bin ${SHELL} ../utils/objfw-embed testfile.bin testfile.bin $@ testfile_ini.m: testfile.ini @@ -154,28 +153,10 @@ rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \ rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll; \ rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \ exit $$EXIT -run-on-ios: all - if [ -z "${IOS_HOST}" ]; then \ - echo "Please set IOS_HOST to the hostname of your iOS host!"; \ - exit 1; \ - fi - echo "Uploading files to iOS device ${IOS_HOST} at ${IOS_TMP}..." - ssh ${IOS_USER}@${IOS_HOST} \ - 'rm -fr ${IOS_TMP} && mkdir -p ${IOS_TMP}/plugin' - destname=libobjfw.${OBJFW_LIB_MAJOR}.dylib; \ - scp -q ../src/libobjfw.dylib \ - ${IOS_USER}@${IOS_HOST}:${IOS_TMP}/$$destname - scp -q tests testfile.txt ${IOS_USER}@${IOS_HOST}:${IOS_TMP}/ - scp -q plugin/TestPlugin.bundle \ - ${IOS_USER}@${IOS_HOST}:${IOS_TMP}/plugin/ - echo "Running tests binary on iOS device ${IOS_HOST}..." - ssh ${IOS_USER}@${IOS_HOST} \ - 'cd ${IOS_TMP} && DYLD_LIBRARY_PATH=. ${WRAPPER} ./tests' - run-on-android: all echo "Uploading files to Android device..." if test -f ../src/libobjfw.so; then \ adb push ../src/libobjfw.so \ /data/local/tmp/objfw/libobjfw.so.${OBJFW_LIB_MAJOR}; \ @@ -200,11 +181,11 @@ pack-pbp $@ PARAM.SFO NULL NULL NULL NULL NULL ${PROG_NOINST} NULL boot.dol: ${PROG_NOINST} elf2dol ${PROG_NOINST} $@ -${PROG_NOINST}: ${LIBOBJFW_DEP} ${LIBOBJFWRT_DEP} +${PROG_NOINST}: ${LIBOBJFW_DEP} ${LIBOBJFWRT_DEP} ../src/test/libobjfwtest.a ${PROG_NOINST}.3dsx: ${PROG_NOINST} 3dsxtool $< $@ ${PROG_NOINST}.arm9: ${PROG_NOINST} @@ -230,12 +211,22 @@ elf2rpl $< $@ CPPFLAGS += -I../src \ -I../src/exceptions \ -I../src/runtime \ + -I../src/test \ -I.. \ - -DSTDOUT \ + -DOBJFWTEST_LOCAL_INCLUDES \ -DPROG_SUFFIX=\"${PROG_SUFFIX}\" OBJCFLAGS_RuntimeARCTests.m = -fobjc-arc -fobjc-arc-exceptions -LIBS := ${TESTS_LIBS} ${LIBS} +# Repetition is required for Wii U, as otherwise it cannot find main. Just +# moving -lobjfwtest later doesn't work either, as then the linker cannot find +# ObjFW symbols. So the only solution is to list everything twice. +LIBS := -L../src/test \ + -lobjfwtest \ + ${TESTS_LIBS} \ + ${LIBS} \ + -lobjfwtest \ + ${TESTS_LIBS} \ + ${LIBS} LDFLAGS += ${MAP_LDFLAGS} LD = ${OBJC} ADDED tests/OFArrayTests.h Index: tests/OFArrayTests.h ================================================================== --- /dev/null +++ tests/OFArrayTests.h @@ -0,0 +1,25 @@ +/* + * 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 "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFArrayTests: OTTestCase +{ + OFArray *_array; +} + +@property (readonly, nonatomic) Class arrayClass; +@end Index: tests/OFArrayTests.m ================================================================== --- tests/OFArrayTests.m +++ tests/OFArrayTests.m @@ -13,68 +13,281 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "OFArrayTests.h" -static OFString *module; +@interface CustomArray: OFArray +{ + OFArray *_array; +} +@end + static OFString *const cArray[] = { @"Foo", @"Bar", @"Baz" }; -@interface SimpleArray: OFArray -{ - OFMutableArray *_array; -} -@end - -@interface SimpleMutableArray: OFMutableArray -{ - OFMutableArray *_array; -} -@end - -@implementation SimpleArray -- (instancetype)init -{ - self = [super init]; - - @try { - _array = [[OFMutableArray alloc] init]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (instancetype)initWithObject: (id)object arguments: (va_list)arguments -{ - self = [super init]; - - @try { - _array = [[OFMutableArray alloc] initWithObject: object - arguments: arguments]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - +@implementation OFArrayTests +- (Class)arrayClass +{ + return [CustomArray class]; +} + +- (void)setUp +{ + [super setUp]; + + _array = [[self.arrayClass alloc] + initWithObjects: cArray + count: sizeof(cArray) / sizeof(*cArray)]; +} + +- (void)dealloc +{ + [_array release]; + + [super dealloc]; +} + +- (void)testArray +{ + OFArray *array = [self.arrayClass array]; + + OTAssertNotNil(array); + OTAssertEqual(array.count, 0); +} + +- (void)testArrayWithObjects +{ + OFArray *array = [self.arrayClass arrayWithObjects: + @"Foo", @"Bar", @"Baz", nil]; + + OTAssertNotNil(array); + OTAssertEqual(array.count, 3); + OTAssertEqualObjects([array objectAtIndex: 0], @"Foo"); + OTAssertEqualObjects([array objectAtIndex: 1], @"Bar"); + OTAssertEqualObjects([array objectAtIndex: 2], @"Baz"); +} + +- (void)testArrayWithObjectsCount +{ + OFArray *array1 = [self.arrayClass arrayWithObjects: + @"Foo", @"Bar", @"Baz", nil]; + OFArray *array2 = [self.arrayClass arrayWithObjects: cArray count: 3]; + + OTAssertEqualObjects(array1, array2); +} + +- (void)testDescription +{ + OTAssertEqualObjects(_array.description, + @"(\n\tFoo,\n\tBar,\n\tBaz\n)"); +} + +- (void)testCount +{ + OTAssertEqual(_array.count, 3); +} + +- (void)testIsEqual +{ + OFArray *array = [self.arrayClass arrayWithObjects: cArray count: 3]; + + OTAssertEqualObjects(array, _array); + OTAssertNotEqual(array, _array); +} + +- (void)testObjectAtIndex +{ + OTAssertEqualObjects([_array objectAtIndex: 0], cArray[0]); + OTAssertEqualObjects([_array objectAtIndex: 1], cArray[1]); + OTAssertEqualObjects([_array objectAtIndex: 2], cArray[2]); +} + +- (void)testObjectAtIndexFailsWhenOutOfRange +{ + OTAssertThrowsSpecific([_array objectAtIndex: _array.count], + OFOutOfRangeException); +} + +- (void)testContainsObject +{ + OTAssertTrue([_array containsObject: cArray[1]]); + OTAssertFalse([_array containsObject: @"nonexistent"]); +} + +- (void)testContainsObjectIdenticalTo +{ + OTAssertTrue([_array containsObjectIdenticalTo: cArray[1]]); + OTAssertFalse([_array containsObjectIdenticalTo: + [OFString stringWithString: cArray[1]]]); +} + +- (void)testIndexOfObject +{ + OTAssertEqual([_array indexOfObject: cArray[1]], 1); + OTAssertEqual([_array indexOfObject: @"nonexistent"], OFNotFound); +} + +- (void)testIndexOfObjectIdenticalTo +{ + OTAssertEqual([_array indexOfObjectIdenticalTo: cArray[1]], 1); + OTAssertEqual([_array indexOfObjectIdenticalTo: + [OFString stringWithString: cArray[1]]], + OFNotFound); +} + +- (void)objectsInRange +{ + OTAssertEqualObjects([_array objectsInRange: OFMakeRange(1, 2)], + ([self.arrayClass arrayWithObjects: cArray[1], cArray[2], nil])); +} + +- (void)testEnumerator +{ + OFEnumerator *enumerator = [_array objectEnumerator]; + + OTAssertEqualObjects([enumerator nextObject], cArray[0]); + OTAssertEqualObjects([enumerator nextObject], cArray[1]); + OTAssertEqualObjects([enumerator nextObject], cArray[2]); + OTAssertNil([enumerator nextObject]); +} + +- (void)testFastEnumeration +{ + size_t i = 0; + + for (OFString *object in _array) { + OTAssertLessThan(i, 3); + OTAssertEqualObjects(object, cArray[i++]); + } +} + +- (void)testComponentsJoinedByString +{ + OFArray *array; + + array = [self.arrayClass arrayWithObjects: @"", @"a", @"b", @"c", nil]; + OTAssertEqualObjects([array componentsJoinedByString: @" "], + @" a b c"); + + array = [self.arrayClass arrayWithObject: @"foo"]; + OTAssertEqualObjects([array componentsJoinedByString: @" "], @"foo"); +} + +- (void)testComponentsJoinedByStringOptions +{ + OFArray *array; + + array = [self.arrayClass + arrayWithObjects: @"", @"foo", @"", @"", @"bar", @"", nil]; + OTAssertEqualObjects( + [array componentsJoinedByString: @" " + options: OFArraySkipEmptyComponents], + @"foo bar"); +} + +- (void)testSortedArray +{ + OFArray *array = [_array arrayByAddingObjectsFromArray: + [OFArray arrayWithObjects: @"0", @"z", nil]]; + + OTAssertEqualObjects([array sortedArray], + ([OFArray arrayWithObjects: @"0", @"Bar", @"Baz", @"Foo", @"z", + nil])); + + OTAssertEqualObjects( + [array sortedArrayUsingSelector: @selector(compare:) + options: OFArraySortDescending], + ([OFArray arrayWithObjects: @"z", @"Foo", @"Baz", @"Bar", @"0", + nil])); +} + +- (void)testReversedArray +{ + OTAssertEqualObjects(_array.reversedArray, + ([OFArray arrayWithObjects: cArray[2], cArray[1], cArray[0], nil])); +} + +#ifdef OF_HAVE_BLOCKS +- (void)testEnumerateObjectsUsingBlock +{ + __block size_t i = 0; + + [_array enumerateObjectsUsingBlock: + ^ (id object, size_t idx, bool *stop) { + OTAssertEqualObjects(object, [_array objectAtIndex: i++]); + }]; + + OTAssertEqual(i, _array.count); +} + +- (void)testMappedArrayUsingBlock +{ + OTAssertEqualObjects( + [_array mappedArrayUsingBlock: ^ id (id object, size_t idx) { + switch (idx) { + case 0: + return @"foobar"; + case 1: + return @"qux"; + } + + return @""; + }].description, + @"(\n\tfoobar,\n\tqux,\n\t\n)"); +} + +- (void)testFilteredArrayUsingBlock +{ + OTAssertEqualObjects( + [_array filteredArrayUsingBlock: ^ bool (id object, size_t idx) { + return [object isEqual: @"Foo"]; + }].description, + @"(\n\tFoo\n)"); + +} + +- (void)testFoldUsingBlock +{ + OTAssertEqualObjects( + [([self.arrayClass arrayWithObjects: [OFMutableString string], + @"foo", @"bar", @"baz", nil]) + foldUsingBlock: ^ id (id left, id right) { + [left appendString: right]; + return left; + }], + @"foobarbaz"); +} +#endif + +- (void)testValueForKey +{ + OTAssertEqualObjects( + [([self.arrayClass arrayWithObjects: @"foo", @"bar", @"quxqux", + nil]) valueForKey: @"length"], + ([self.arrayClass arrayWithObjects: [OFNumber numberWithInt: 3], + [OFNumber numberWithInt: 3], [OFNumber numberWithInt: 6], nil])); + + OTAssertEqualObjects( + [([self.arrayClass arrayWithObjects: @"1", @"2", nil]) + valueForKey: @"@count"], + [OFNumber numberWithInt: 2]); +} +@end + +@implementation CustomArray - (instancetype)initWithObjects: (id const *)objects count: (size_t)count { self = [super init]; @try { - _array = [[OFMutableArray alloc] initWithObjects: objects - count: count]; + _array = [[OFArray alloc] initWithObjects: objects + count: count]; } @catch (id e) { [self release]; @throw e; } @@ -95,364 +308,6 @@ - (size_t)count { return [_array count]; } -@end - -@implementation SimpleMutableArray -+ (void)initialize -{ - if (self == [SimpleMutableArray class]) - [self inheritMethodsFromClass: [SimpleArray class]]; -} - -- (void)insertObject: (id)object atIndex: (size_t)idx -{ - [_array insertObject: object atIndex: idx]; -} - -- (void)replaceObjectAtIndex: (size_t)idx withObject: (id)object -{ - [_array replaceObjectAtIndex: idx withObject: object]; -} - -- (void)removeObjectAtIndex: (size_t)idx -{ - [_array removeObjectAtIndex: idx]; -} -@end - -@implementation TestsAppDelegate (OFArrayTests) -- (void)arrayTestsWithClass: (Class)arrayClass - mutableClass: (Class)mutableArrayClass -{ - void *pool = objc_autoreleasePoolPush(); - OFArray *array1, *array2; - OFMutableArray *mutableArray1, *mutableArray2; - OFEnumerator *enumerator; - id object; - bool ok; - size_t i; - - TEST(@"+[array]", (mutableArray1 = [mutableArrayClass array])) - - TEST(@"+[arrayWithObjects:]", - (array1 = - [arrayClass arrayWithObjects: @"Foo", @"Bar", @"Baz", nil])) - - TEST(@"+[arrayWithObjects:count:]", - (array2 = [arrayClass arrayWithObjects: cArray count: 3]) && - [array2 isEqual: array1]) - - TEST(@"-[description]", - [array1.description isEqual: @"(\n\tFoo,\n\tBar,\n\tBaz\n)"]) - - TEST(@"-[addObject:]", - R([mutableArray1 addObject: cArray[0]]) && - R([mutableArray1 addObject: cArray[2]])) - - TEST(@"-[insertObject:atIndex:]", - R([mutableArray1 insertObject: cArray[1] atIndex: 1])) - - TEST(@"-[count]", - mutableArray1.count == 3 && array1.count == 3 && array2.count == 3) - - TEST(@"-[isEqual:]", - [mutableArray1 isEqual: array1] && [array1 isEqual: array2]) - - TEST(@"-[objectAtIndex:]", - [[mutableArray1 objectAtIndex: 0] isEqual: cArray[0]] && - [[mutableArray1 objectAtIndex: 1] isEqual: cArray[1]] && - [[mutableArray1 objectAtIndex: 2] isEqual: cArray[2]] && - [[array1 objectAtIndex: 0] isEqual: cArray[0]] && - [[array1 objectAtIndex: 1] isEqual: cArray[1]] && - [[array1 objectAtIndex: 2] isEqual: cArray[2]] && - [[array2 objectAtIndex: 0] isEqual: cArray[0]] && - [[array2 objectAtIndex: 1] isEqual: cArray[1]] && - [[array2 objectAtIndex: 2] isEqual: cArray[2]]) - - TEST(@"-[containsObject:]", - [array1 containsObject: cArray[1]] && - ![array1 containsObject: @"nonexistent"]) - - TEST(@"-[containsObjectIdenticalTo:]", - [array1 containsObjectIdenticalTo: cArray[1]] && - ![array1 containsObjectIdenticalTo: - [OFString stringWithString: cArray[1]]]) - - TEST(@"-[indexOfObject:]", [array1 indexOfObject: cArray[1]] == 1) - - TEST(@"-[indexOfObjectIdenticalTo:]", - [array2 indexOfObjectIdenticalTo: cArray[1]] == 1) - - TEST(@"-[objectsInRange:]", - [[array1 objectsInRange: OFMakeRange(1, 2)] isEqual: - [arrayClass arrayWithObjects: cArray[1], cArray[2], nil]]) - - TEST(@"-[replaceObject:withObject:]", - R([mutableArray1 replaceObject: cArray[1] withObject: cArray[0]]) && - [[mutableArray1 objectAtIndex: 0] isEqual: cArray[0]] && - [[mutableArray1 objectAtIndex: 1] isEqual: cArray[0]] && - [[mutableArray1 objectAtIndex: 2] isEqual: cArray[2]]) - - TEST(@"-[replaceObject:identicalTo:]", - R([mutableArray1 replaceObjectIdenticalTo: cArray[0] - withObject: cArray[1]]) && - [[mutableArray1 objectAtIndex: 0] isEqual: cArray[1]] && - [[mutableArray1 objectAtIndex: 1] isEqual: cArray[0]] && - [[mutableArray1 objectAtIndex: 2] isEqual: cArray[2]]) - - TEST(@"-[replaceObjectAtIndex:withObject:]", - R([mutableArray1 replaceObjectAtIndex: 0 withObject: cArray[0]]) && - [[mutableArray1 objectAtIndex: 0] isEqual: cArray[0]] && - [[mutableArray1 objectAtIndex: 1] isEqual: cArray[0]] && - [[mutableArray1 objectAtIndex: 2] isEqual: cArray[2]]) - - TEST(@"-[removeObject:]", - R([mutableArray1 removeObject: cArray[0]]) && - mutableArray1.count == 1) - - [mutableArray1 addObject: cArray[0]]; - - TEST(@"-[removeObjectIdenticalTo:]", - R([mutableArray1 removeObjectIdenticalTo: cArray[2]]) && - mutableArray1.count == 1) - - mutableArray2 = [[array1 mutableCopy] autorelease]; - TEST(@"-[removeObjectAtIndex:]", - R([mutableArray2 removeObjectAtIndex: 1]) && - mutableArray2.count == 2 && - [[mutableArray2 objectAtIndex: 1] isEqual: cArray[2]]) - - mutableArray2 = [[array1 mutableCopy] autorelease]; - TEST(@"-[removeObjectsInRange:]", - R([mutableArray2 removeObjectsInRange: OFMakeRange(0, 2)]) && - mutableArray2.count == 1 && - [[mutableArray2 objectAtIndex: 0] isEqual: cArray[2]]) - - mutableArray2 = [[array1 mutableCopy] autorelease]; - [mutableArray2 addObject: @"qux"]; - [mutableArray2 addObject: @"last"]; - TEST(@"-[reverse]", - R([mutableArray2 reverse]) && - [mutableArray2 isEqual: [arrayClass arrayWithObjects: - @"last", @"qux", @"Baz", @"Bar", @"Foo", nil]]) - - mutableArray2 = [[array1 mutableCopy] autorelease]; - [mutableArray2 addObject: @"qux"]; - [mutableArray2 addObject: @"last"]; - TEST(@"-[reversedArray]", - [[mutableArray2 reversedArray] isEqual: - [arrayClass arrayWithObjects: - @"last", @"qux", @"Baz", @"Bar", @"Foo", nil]]) - - mutableArray2 = [[array1 mutableCopy] autorelease]; - [mutableArray2 addObject: @"0"]; - [mutableArray2 addObject: @"z"]; - TEST(@"-[sortedArray]", - [[mutableArray2 sortedArray] isEqual: [arrayClass arrayWithObjects: - @"0", @"Bar", @"Baz", @"Foo", @"z", nil]] && - [[mutableArray2 sortedArrayUsingSelector: @selector(compare:) - options: OFArraySortDescending] - isEqual: [arrayClass arrayWithObjects: - @"z", @"Foo", @"Baz", @"Bar", @"0", nil]]) - - EXPECT_EXCEPTION(@"Detect out of range in -[objectAtIndex:]", - OFOutOfRangeException, [array1 objectAtIndex: array1.count]) - - EXPECT_EXCEPTION(@"Detect out of range in -[removeObjectsInRange:]", - OFOutOfRangeException, [mutableArray1 removeObjectsInRange: - OFMakeRange(0, mutableArray1.count + 1)]) - - TEST(@"-[componentsJoinedByString:]", - (array2 = [arrayClass arrayWithObjects: @"", @"a", @"b", @"c", - nil]) && - [[array2 componentsJoinedByString: @" "] isEqual: @" a b c"] && - (array2 = [arrayClass arrayWithObject: @"foo"]) && - [[array2 componentsJoinedByString: @" "] isEqual: @"foo"]) - - TEST(@"-[componentsJoinedByString:options]", - (array2 = [arrayClass arrayWithObjects: @"", @"foo", @"", @"", - @"bar", @"", nil]) && - [[array2 componentsJoinedByString: @" " - options: OFArraySkipEmptyComponents] - isEqual: @"foo bar"]) - - mutableArray1 = [[array1 mutableCopy] autorelease]; - ok = true; - i = 0; - - TEST(@"-[objectEnumerator]", - (enumerator = [mutableArray1 objectEnumerator])) - - while ((object = [enumerator nextObject]) != nil) { - if (![object isEqual: cArray[i]]) - ok = false; - [mutableArray1 replaceObjectAtIndex: i withObject: @""]; - i++; - } - - if (mutableArray1.count != i) - ok = false; - - TEST(@"OFEnumerator's -[nextObject]", ok) - - [mutableArray1 removeObjectAtIndex: 0]; - - EXPECT_EXCEPTION(@"Detection of mutation during enumeration", - OFEnumerationMutationException, [enumerator nextObject]) - - mutableArray1 = [[array1 mutableCopy] autorelease]; - ok = true; - i = 0; - - for (OFString *string in mutableArray1) { - if (![string isEqual: cArray[i]]) - ok = false; - [mutableArray1 replaceObjectAtIndex: i withObject: @""]; - i++; - } - - if (mutableArray1.count != i) - ok = false; - - TEST(@"Fast Enumeration", ok) - - [mutableArray1 replaceObjectAtIndex: 0 withObject: cArray[0]]; - [mutableArray1 replaceObjectAtIndex: 1 withObject: cArray[1]]; - [mutableArray1 replaceObjectAtIndex: 2 withObject: cArray[2]]; - - ok = false; - i = 0; - @try { - for (OFString *string in mutableArray1) { - (void)string; - - if (i == 0) - [mutableArray1 addObject: @""]; - - i++; - } - } @catch (OFEnumerationMutationException *e) { - ok = true; - } - - TEST(@"Detection of mutation during Fast Enumeration", ok) - - [mutableArray1 removeLastObject]; - -#ifdef OF_HAVE_BLOCKS - { - __block bool blockOK = true; - __block size_t count = 0; - OFArray *compareArray = array1; - OFMutableArray *mutableArray3; - - mutableArray1 = [[array1 mutableCopy] autorelease]; - [mutableArray1 enumerateObjectsUsingBlock: - ^ (id object_, size_t idx, bool *stop) { - count++; - if (![object_ isEqual: - [compareArray objectAtIndex: idx]]) - blockOK = false; - }]; - - if (count != compareArray.count) - blockOK = false; - - TEST(@"Enumeration using blocks", blockOK) - - blockOK = false; - mutableArray3 = mutableArray1; - @try { - [mutableArray3 enumerateObjectsUsingBlock: - ^ (id object_, size_t idx, bool *stop) { - [mutableArray3 removeObjectAtIndex: idx]; - }]; - } @catch (OFEnumerationMutationException *e) { - blockOK = true; - } @catch (OFOutOfRangeException *e) { - /* - * Out of bounds access due to enumeration not being - * detected. - */ - } - - TEST(@"Detection of mutation during enumeration using blocks", - blockOK) - } - - TEST(@"-[replaceObjectsUsingBlock:]", - R([mutableArray1 replaceObjectsUsingBlock: - ^ id (id object_, size_t idx) { - switch (idx) { - case 0: - return @"foo"; - case 1: - return @"bar"; - } - - return nil; - }]) && [mutableArray1.description isEqual: @"(\n\tfoo,\n\tbar\n)"]) - - TEST(@"-[mappedArrayUsingBlock:]", - [[mutableArray1 mappedArrayUsingBlock: - ^ id (id object_, size_t idx) { - switch (idx) { - case 0: - return @"foobar"; - case 1: - return @"qux"; - } - - return nil; - }].description isEqual: @"(\n\tfoobar,\n\tqux\n)"]) - - TEST(@"-[filteredArrayUsingBlock:]", - [[mutableArray1 filteredArrayUsingBlock: - ^ bool (id object_, size_t idx) { - return [object_ isEqual: @"foo"]; - }].description isEqual: @"(\n\tfoo\n)"]) - - TEST(@"-[foldUsingBlock:]", - [[arrayClass arrayWithObjects: [OFMutableString string], @"foo", - @"bar", @"baz", nil] foldUsingBlock: ^ id (id left, id right) { - [left appendString: right]; - return left; - }]) -#endif - - TEST(@"-[valueForKey:]", - [[[arrayClass arrayWithObjects: @"foo", @"bar", @"quxqux", nil] - valueForKey: @"length"] isEqual: - [arrayClass arrayWithObjects: [OFNumber numberWithInt: 3], - [OFNumber numberWithInt: 3], [OFNumber numberWithInt: 6], nil]] && - [[[arrayClass arrayWithObjects: @"1", @"2", nil] - valueForKey: @"@count"] isEqual: [OFNumber numberWithInt: 2]]) - - mutableArray1 = [mutableArrayClass arrayWithObjects: - [OFMutableIRI IRIWithString: @"http://foo.bar/"], - [OFMutableIRI IRIWithString: @"http://bar.qux/"], - [OFMutableIRI IRIWithString: @"http://qux.quxqux/"], nil]; - TEST(@"-[setValue:forKey:]", - R([mutableArray1 setValue: [OFNumber numberWithShort: 1234] - forKey: @"port"]) && - [mutableArray1 isEqual: [arrayClass arrayWithObjects: - [OFIRI IRIWithString: @"http://foo.bar:1234/"], - [OFIRI IRIWithString: @"http://bar.qux:1234/"], - [OFIRI IRIWithString: @"http://qux.quxqux:1234/"], nil]]) - - objc_autoreleasePoolPop(pool); -} - -- (void)arrayTests -{ - module = @"OFArray"; - [self arrayTestsWithClass: [SimpleArray class] - mutableClass: [SimpleMutableArray class]]; - - module = @"OFArray_adjacent"; - [self arrayTestsWithClass: [OFArray class] - mutableClass: [OFMutableArray class]]; -} @end Index: tests/OFBlockTests.m ================================================================== --- tests/OFBlockTests.m +++ tests/OFBlockTests.m @@ -13,13 +13,15 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFBlock"; +@interface OFBlockTests: OTTestCase +@end #if defined(OF_OBJFW_RUNTIME) extern struct objc_class _NSConcreteStackBlock; extern struct objc_class _NSConcreteGlobalBlock; extern struct objc_class _NSConcreteMallocBlock; @@ -54,66 +56,83 @@ Block_release(block); return d; } -@implementation TestsAppDelegate (OFBlockTests) -- (void)blockTests +@implementation OFBlockTests +- (void)testClassOfStackBlock +{ + __block int x; + void (^stackBlock)(void) = ^ { + x = 0; + (void)x; + }; + + OTAssertEqual((Class)&_NSConcreteStackBlock, + objc_getClass("OFStackBlock")); + OTAssertTrue([stackBlock isKindOfClass: [OFBlock class]]); +} + +#if !defined(OF_WINDOWS) || !defined(__clang__) +- (void)testClassOfGlobalBlock +{ + OTAssertEqual((Class)&_NSConcreteGlobalBlock, + objc_getClass("OFGlobalBlock")); + OTAssertTrue([globalBlock isKindOfClass: [OFBlock class]]); +} +#endif + +- (void)testClassOfMallocBlock +{ + OTAssertEqual((Class)&_NSConcreteMallocBlock, + objc_getClass("OFMallocBlock")); +} + +- (void)testCopyStackBlock +{ + __block int x; + void (^stackBlock)(void) = ^ { + x = 0; + (void)x; + }; + void (^mallocBlock)(void); + + mallocBlock = [[stackBlock copy] autorelease]; + OTAssertEqual([mallocBlock class], objc_getClass("OFMallocBlock")); + OTAssertTrue([mallocBlock isKindOfClass: [OFBlock class]]); +} + +- (void)testCopyStackBlockAndReferenceVariable +{ + OTAssertEqual(forwardTest(), 5); +} + +- (void)testCopyStackBlockAndReferenceCopiedVariable +{ + int (^voidBlock)(void) = returnStackBlock(); + + OTAssertEqual(voidBlock(), 43); + OTAssertEqual(voidBlock(), 44); + OTAssertEqual(voidBlock(), 45); +} + +#if !defined(OF_WINDOWS) || !defined(__clang__) +- (void)testCopyGlobalBlock { - void *pool = objc_autoreleasePoolPush(); + OTAssertEqual([[globalBlock copy] autorelease], (id)globalBlock); +} +#endif + +- (void)testCopyMallocBlock +{ __block int x; void (^stackBlock)(void) = ^ { x = 0; (void)x; }; void (^mallocBlock)(void); - int (^voidBlock)(void); - - TEST(@"Class of stack block", - (Class)&_NSConcreteStackBlock == objc_getClass("OFStackBlock") && - [stackBlock isKindOfClass: [OFBlock class]]) - -#if !defined(OF_WINDOWS) || !defined(__clang__) - TEST(@"Class of global block", - (Class)&_NSConcreteGlobalBlock == objc_getClass("OFGlobalBlock") && - [globalBlock isKindOfClass: [OFBlock class]]) -#endif - - TEST(@"Class of a malloc block", - (Class)&_NSConcreteMallocBlock == objc_getClass("OFMallocBlock")) - - TEST(@"Copying a stack block", - (mallocBlock = [[stackBlock copy] autorelease]) && - [mallocBlock class] == objc_getClass("OFMallocBlock") && - [mallocBlock isKindOfClass: [OFBlock class]]) - - TEST(@"Copying a stack block and referencing its variable", - forwardTest() == 5) - - TEST(@"Copying a stack block and using its copied variable", - (voidBlock = returnStackBlock()) && voidBlock() == 43 && - voidBlock() == 44 && voidBlock() == 45) - -#if !defined(OF_WINDOWS) || !defined(__clang__) - TEST(@"Copying a global block", - (id)globalBlock == [[globalBlock copy] autorelease]) -#endif - -#ifndef __clang_analyzer__ - TEST(@"Copying a malloc block", - (id)mallocBlock == [mallocBlock copy] && - [mallocBlock retainCount] == 2) -#endif - - TEST(@"Autorelease a stack block", R([stackBlock autorelease])) - -#if !defined(OF_WINDOWS) || !defined(__clang__) - TEST(@"Autorelease a global block", R([globalBlock autorelease])) -#endif - -#ifndef __clang_analyzer__ - TEST(@"Autorelease a malloc block", R([mallocBlock autorelease])) -#endif - - objc_autoreleasePoolPop(pool); + + mallocBlock = [[stackBlock copy] autorelease]; + OTAssertEqual([[mallocBlock copy] autorelease], (id)mallocBlock); + OTAssertEqual([mallocBlock retainCount], 2); } @end Index: tests/OFCharacterSetTests.m ================================================================== --- tests/OFCharacterSetTests.m +++ tests/OFCharacterSetTests.m @@ -13,95 +13,88 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" #import "OFCharacterSet.h" #import "OFBitSetCharacterSet.h" #import "OFRangeCharacterSet.h" -static OFString *module; +@interface OFCharacterSetTests: OTTestCase +@end -@interface SimpleCharacterSet: OFCharacterSet +@interface CustomCharacterSet: OFCharacterSet @end -@implementation SimpleCharacterSet +@implementation CustomCharacterSet - (bool)characterIsMember: (OFUnichar)character { return (character % 2 == 0); } @end -@implementation TestsAppDelegate (OFCharacterSetTests) -- (void)characterSetTests -{ - void *pool = objc_autoreleasePoolPush(); - OFCharacterSet *characterSet, *invertedCharacterSet; - bool ok; - - module = @"OFCharacterSet"; - - characterSet = [[[SimpleCharacterSet alloc] init] autorelease]; - - ok = true; - for (OFUnichar c = 0; c < 65536; c++) { - if (c % 2 == 0) { - if (![characterSet characterIsMember: c]) - ok = false; - } else if ([characterSet characterIsMember: c]) - ok = false; - } - TEST(@"-[characterIsMember:]", ok); - - module = @"OFBitSetCharacterSet"; - - TEST(@"+[characterSetWithCharactersInString:]", - (characterSet = [OFCharacterSet characterSetWithCharactersInString: - @"0123456789"]) && - [characterSet isKindOfClass: [OFBitSetCharacterSet class]]) - - ok = true; - for (OFUnichar c = 0; c < 65536; c++) { - if (c >= '0' && c <= '9') { - if (![characterSet characterIsMember: c]) - ok = false; - } else if ([characterSet characterIsMember: c]) - ok = false; - } - TEST(@"-[characterIsMember:]", ok); - - module = @"OFRangeCharacterSet"; - - TEST(@"+[characterSetWithRange:]", - (characterSet = [OFCharacterSet - characterSetWithRange: OFMakeRange('0', 10)]) && - [characterSet isKindOfClass: [OFRangeCharacterSet class]]) - - ok = true; - for (OFUnichar c = 0; c < 65536; c++) { - if (c >= '0' && c <= '9') { - if (![characterSet characterIsMember: c]) - ok = false; - } else if ([characterSet characterIsMember: c]) - ok = false; - } - TEST(@"-[characterIsMember:]", ok); - - ok = true; - invertedCharacterSet = characterSet.invertedSet; - for (OFUnichar c = 0; c < 65536; c++) { - if (c >= '0' && c <= '9') { - if ([invertedCharacterSet characterIsMember: c]) - ok = false; - } else if (![invertedCharacterSet characterIsMember: c]) - ok = false; - } - TEST(@"-[invertedSet]", ok); - - TEST(@"Inverting -[invertedSet] returns original set", - invertedCharacterSet.invertedSet == characterSet) - - objc_autoreleasePoolPop(pool); +@implementation OFCharacterSetTests +- (void)testCustomCharacterSet +{ + OFCharacterSet *characterSet = + [[[CustomCharacterSet alloc] init] autorelease]; + + for (OFUnichar c = 0; c < 65536; c++) + if (c % 2 == 0) + OTAssertTrue([characterSet characterIsMember: c]); + else + OTAssertFalse([characterSet characterIsMember: c]); +} + +- (void)testBitSetCharacterSet +{ + OFCharacterSet *characterSet = + [OFCharacterSet characterSetWithCharactersInString: @"0123456789"]; + + OTAssertTrue( + [characterSet isKindOfClass: [OFBitSetCharacterSet class]]); + + for (OFUnichar c = 0; c < 65536; c++) + if (c >= '0' && c <= '9') + OTAssertTrue([characterSet characterIsMember: c]); + else if ([characterSet characterIsMember: c]) + OTAssertFalse([characterSet characterIsMember: c]); +} + +- (void)testRangeCharacterSet +{ + OFCharacterSet *characterSet = + [OFCharacterSet characterSetWithRange: OFMakeRange('0', 10)]; + + OTAssertTrue( + [characterSet isKindOfClass: [OFRangeCharacterSet class]]); + + for (OFUnichar c = 0; c < 65536; c++) + if (c >= '0' && c <= '9') + OTAssertTrue([characterSet characterIsMember: c]); + else + OTAssertFalse([characterSet characterIsMember: c]); +} + +- (void)testInvertedCharacterSet +{ + OFCharacterSet *characterSet = [[OFCharacterSet + characterSetWithRange: OFMakeRange('0', 10)] invertedSet]; + + for (OFUnichar c = 0; c < 65536; c++) + if (c >= '0' && c <= '9') + OTAssertFalse([characterSet characterIsMember: c]); + else + OTAssertTrue([characterSet characterIsMember: c]); +} + +- (void)testInvertingInvertedSetReturnsOriginal +{ + OFCharacterSet *characterSet = + [OFCharacterSet characterSetWithRange: OFMakeRange('0', 10)]; + + OTAssertEqual(characterSet, characterSet.invertedSet.invertedSet); } @end Index: tests/OFColorTests.m ================================================================== --- tests/OFColorTests.m +++ tests/OFColorTests.m @@ -13,35 +13,50 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFColor"; - -@implementation TestsAppDelegate (OFColorTests) -- (void)colorTests -{ - void *pool = objc_autoreleasePoolPush(); - OFColor *color; - float red, green, blue, alpha; - - TEST(@"+[colorWithRed:green:blue:alpha:]", - (color = [OFColor colorWithRed: 63.f / 255 - green: 127.f / 255 - blue: 1 - alpha: 1])) +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFColorTests: OTTestCase +{ + OFColor *_color; +} +@end + +@implementation OFColorTests +- (void)setUp +{ + [super setUp]; + + _color = [[OFColor alloc] initWithRed: 63.f / 255 + green: 127.f / 255 + blue: 1 + alpha: 1]; +} + +- (void)dealloc +{ + [_color release]; + + [super dealloc]; +} #ifdef OF_OBJFW_RUNTIME - TEST(@"+[colorWithRed:green:blue:alpha:] returns tagged pointer", - object_isTaggedPointer(color)) +- (void)testReturnsTaggedPointer +{ + OTAssertTrue(object_isTaggedPointer(_color)); +} #endif - TEST(@"-[getRed:green:blue:alpha:]", - R([color getRed: &red green: &green blue: &blue alpha: &alpha]) && - red == 63.f / 255 && green == 127.f / 255 && blue == 1 && - alpha == 1) +- (void)testGetRedGreenBlueAlpha +{ + float red, green, blue, alpha; - objc_autoreleasePoolPop(pool); + [_color getRed: &red green: &green blue: &blue alpha: &alpha]; + OTAssertEqual(red, 63.f / 255); + OTAssertEqual(green, 127.f / 255); + OTAssertEqual(blue, 1); + OTAssertEqual(alpha, 1); } @end ADDED tests/OFConcreteArrayTests.m Index: tests/OFConcreteArrayTests.m ================================================================== --- /dev/null +++ tests/OFConcreteArrayTests.m @@ -0,0 +1,30 @@ +/* + * 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 "OFArrayTests.h" + +#import "OFConcreteArray.h" + +@interface OFConcreteArrayTests: OFArrayTests +@end + +@implementation OFConcreteArrayTests +- (Class)arrayClass +{ + return [OFConcreteArray class]; +} +@end ADDED tests/OFConcreteDictionaryTests.m Index: tests/OFConcreteDictionaryTests.m ================================================================== --- /dev/null +++ tests/OFConcreteDictionaryTests.m @@ -0,0 +1,30 @@ +/* + * 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 "OFDictionaryTests.h" + +#import "OFConcreteDictionary.h" + +@interface OFConcreteDictionaryTests: OFDictionaryTests +@end + +@implementation OFConcreteDictionaryTests +- (Class)dictionaryClass +{ + return [OFConcreteDictionary class]; +} +@end ADDED tests/OFConcreteMutableArrayTests.m Index: tests/OFConcreteMutableArrayTests.m ================================================================== --- /dev/null +++ tests/OFConcreteMutableArrayTests.m @@ -0,0 +1,105 @@ +/* + * 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 "OFMutableArrayTests.h" + +#import "OFConcreteMutableArray.h" + +@interface OFConcreteMutableArrayTests: OFMutableArrayTests +@end + +static OFString *const cArray[] = { + @"Foo", + @"Bar", + @"Baz" +}; + +@implementation OFConcreteMutableArrayTests +- (Class)arrayClass +{ + return [OFConcreteMutableArray class]; +} + +- (void)testDetectMutationDuringEnumeration +{ + OFEnumerator *enumerator = [_mutableArray objectEnumerator]; + OFString *object; + size_t i; + + i = 0; + while ((object = [enumerator nextObject]) != nil) { + OTAssertEqualObjects(object, cArray[i]); + + [_mutableArray replaceObjectAtIndex: i withObject: @""]; + i++; + } + OTAssertEqual(i, _mutableArray.count); + + [_mutableArray removeObjectAtIndex: 0]; + OTAssertThrowsSpecific([enumerator nextObject], + OFEnumerationMutationException); +} + +- (void)testDetectMutationDuringFastEnumeration +{ + bool detected = false; + size_t i; + + i = 0; + for (OFString *object in _mutableArray) { + OTAssertEqualObjects(object, cArray[i]); + + [_mutableArray replaceObjectAtIndex: i withObject: @""]; + i++; + } + OTAssertEqual(i, _mutableArray.count); + + @try { + for (OFString *object in _mutableArray) { + (void)object; + [_mutableArray removeObjectAtIndex: 0]; + } + } @catch (OFEnumerationMutationException *e) { + detected = true; + } + OTAssertTrue(detected); +} + +#ifdef OF_HAVE_BLOCKS +- (void)testDetectMutationDuringEnumerateObjectsUsingBlock +{ + __block size_t i; + + i = 0; + [_mutableArray enumerateObjectsUsingBlock: + ^ (id object, size_t idx, bool *stop) { + OTAssertEqualObjects(object, cArray[idx]); + + [_mutableArray replaceObjectAtIndex: idx withObject: @""]; + i++; + }]; + OTAssertEqual(i, _mutableArray.count); + + OTAssertThrowsSpecific( + [_mutableArray enumerateObjectsUsingBlock: + ^ (id object, size_t idx, bool *stop) { + [_mutableArray removeObjectAtIndex: 0]; + }], + OFEnumerationMutationException); +} +#endif +@end ADDED tests/OFConcreteMutableDictionaryTests.m Index: tests/OFConcreteMutableDictionaryTests.m ================================================================== --- /dev/null +++ tests/OFConcreteMutableDictionaryTests.m @@ -0,0 +1,100 @@ +/* + * 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 "OFMutableDictionaryTests.h" + +#import "OFConcreteMutableDictionary.h" + +@interface OFConcreteMutableDictionaryTests: OFMutableDictionaryTests +@end + +@implementation OFConcreteMutableDictionaryTests +- (Class)dictionaryClass +{ + return [OFConcreteMutableDictionary class]; +} + +- (void)testDetectMutationDuringEnumeration +{ + OFMutableDictionary *mutableDictionary = + [[_dictionary mutableCopy] autorelease]; + OFEnumerator *keyEnumerator = [mutableDictionary keyEnumerator]; + OFEnumerator *objectEnumerator = [mutableDictionary objectEnumerator]; + OFString *key; + size_t i; + + i = 0; + while ((key = [keyEnumerator nextObject]) != nil) { + [mutableDictionary setObject: @"test" forKey: key]; + i++; + } + OTAssertEqual(i, mutableDictionary.count); + + [mutableDictionary removeObjectForKey: @"key2"]; + OTAssertThrowsSpecific([keyEnumerator nextObject], + OFEnumerationMutationException); + OTAssertThrowsSpecific([objectEnumerator nextObject], + OFEnumerationMutationException); +} + +- (void)testDetectMutationDuringFastEnumeration +{ + OFMutableDictionary *mutableDictionary = + [[_dictionary mutableCopy] autorelease]; + bool detected = false; + size_t i; + + i = 0; + for (OFString *key in mutableDictionary) { + [mutableDictionary setObject: @"test" forKey: key]; + i++; + } + OTAssertEqual(i, mutableDictionary.count); + + @try { + for (OFString *key in mutableDictionary) + [mutableDictionary removeObjectForKey: key]; + } @catch (OFEnumerationMutationException *e) { + detected = true; + } + OTAssertTrue(detected); +} + +#ifdef OF_HAVE_BLOCKS +- (void)testDetectMutationDuringEnumerateObjectsUsingBlock +{ + OFMutableDictionary *mutableDictionary = + [[_dictionary mutableCopy] autorelease]; + __block size_t i; + + i = 0; + [mutableDictionary enumerateKeysAndObjectsUsingBlock: + ^ (id key, id object, bool *stop) { + [mutableDictionary setObject: @"test" forKey: key]; + i++; + }]; + OTAssertEqual(i, mutableDictionary.count); + + OTAssertThrowsSpecific( + [mutableDictionary enumerateKeysAndObjectsUsingBlock: + ^ (id key, id object, bool *stop) { + [mutableDictionary removeObjectForKey: key]; + }], + OFEnumerationMutationException); +} +#endif +@end ADDED tests/OFConcreteMutableSetTests.m Index: tests/OFConcreteMutableSetTests.m ================================================================== --- /dev/null +++ tests/OFConcreteMutableSetTests.m @@ -0,0 +1,65 @@ +/* + * 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 "OFMutableSetTests.h" + +#import "OFConcreteMutableSet.h" + +@interface OFConcreteMutableSetTests: OFMutableSetTests +@end + +@implementation OFConcreteMutableSetTests +- (Class)setClass +{ + return [OFConcreteMutableSet class]; +} + +- (void)testDetectMutationDuringEnumeration +{ + OFEnumerator *enumerator = [_mutableSet objectEnumerator]; + + [_mutableSet removeObject: @"foo"]; + + OTAssertThrowsSpecific([enumerator nextObject], + OFEnumerationMutationException); +} + +- (void)testDetectMutationDuringFastEnumeration +{ + bool detected = false; + + @try { + for (OFString *object in _mutableSet) + [_mutableSet removeObject: object]; + } @catch (OFEnumerationMutationException *e) { + detected = true; + } + + OTAssertTrue(detected); +} + +#ifdef OF_HAVE_BLOCKS +- (void)testDetectMutationDuringEnumerateObjectsUsingBlock +{ + OTAssertThrowsSpecific( + [_mutableSet enumerateObjectsUsingBlock: ^ (id object, bool *stop) { + [_mutableSet removeObject: object]; + }], + OFEnumerationMutationException); +} +#endif +@end ADDED tests/OFConcreteSetTests.m Index: tests/OFConcreteSetTests.m ================================================================== --- /dev/null +++ tests/OFConcreteSetTests.m @@ -0,0 +1,30 @@ +/* + * 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 "OFSetTests.h" + +#import "OFConcreteSet.h" + +@interface OFConcreteSetTests: OFSetTests +@end + +@implementation OFConcreteSetTests +- (Class)setClass +{ + return [OFConcreteSet class]; +} +@end ADDED tests/OFCryptographicHashTests.m Index: tests/OFCryptographicHashTests.m ================================================================== --- /dev/null +++ tests/OFCryptographicHashTests.m @@ -0,0 +1,140 @@ +/* + * 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" + +#include + +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFCryptographicHashTests: OTTestCase +{ + OFStream *_stream; +} +@end + +const unsigned char testFileMD5[16] = + "\x00\x8B\x9D\x1B\x58\xDF\xF8\xFE\xEE\xF3\xAE\x8D\xBB\x68\x2D\x38"; +const unsigned char testFileRIPEMD160[20] = + "\x46\x02\x97\xF5\x85\xDF\xB9\x21\x00\xC8\xF9\x87\xC6\xEC\x84\x0D" + "\xCE\xE6\x08\x8B"; +const unsigned char testFileSHA1[20] = + "\xC9\x9A\xB8\x7E\x1E\xC8\xEC\x65\xD5\xEB\xE4\x2E\x0D\xA6\x80\x96" + "\xF5\x94\xE7\x17"; +const unsigned char testFileSHA224[28] = + "\x27\x69\xD8\x04\x2D\x0F\xCA\x84\x6C\xF1\x62\x44\xBA\x0C\xBD\x46" + "\x64\x5F\x4F\x20\x02\x4D\x15\xED\x1C\x61\x1F\xF7"; +const unsigned char testFileSHA256[32] = + "\x1A\x02\xD6\x46\xF5\xA6\xBA\xAA\xFF\x7F\xD5\x87\xBA\xC3\xF6\xC6" + "\xB5\x67\x93\x8F\x0F\x44\x90\xB8\xF5\x35\x89\xF0\x5A\x23\x7F\x69"; +const unsigned char testFileSHA384[48] = + "\x7E\xDE\x62\xE2\x10\xA5\x1E\x18\x8A\x11\x7F\x78\xD7\xC7\x55\xB6" + "\x43\x94\x1B\xD2\x78\x5C\xCF\xF3\x8A\xB8\x98\x22\xC7\x0E\xFE\xF1" + "\xEC\x53\xE9\x1A\xB3\x51\x70\x8C\x1F\x3F\x56\x12\x44\x01\x91\x54"; +const unsigned char testFileSHA512[64] = + "\x8F\x36\x6E\x3C\x19\x4B\xBB\xC7\x82\xAA\xCD\x7D\x55\xA2\xD3\x29" + "\x29\x97\x6A\x3F\xEB\x9B\xB2\xCB\x75\xC9\xEC\xC8\x10\x07\xD6\x07" + "\x31\x4A\xB1\x30\x97\x82\x58\xA5\x1F\x71\x42\xE6\x56\x07\x99\x57" + "\xB2\xB8\x3B\xA1\x8A\x41\x64\x33\x69\x21\x8C\x2A\x44\x6D\xF2\xA0"; + +@implementation OFCryptographicHashTests +- (void)setUp +{ + OFIRI *IRI; + + [super setUp]; + + IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; + _stream = [[OFIRIHandler openItemAtIRI: IRI mode: @"r"] retain]; +} + +- (void)tearDown +{ + [_stream close]; + + [super tearDown]; +} + +- (void)dealloc +{ + [_stream release]; + + [super dealloc]; +} + +- (void)testHash: (Class)hashClass + expectedDigest: (const unsigned char *)expectedDigest +{ + id hash = + [hashClass hashWithAllowsSwappableMemory: true]; + id copy; + + OTAssertNotNil(hash); + + while (!_stream.atEndOfStream) { + char buffer[64]; + size_t length = [_stream readIntoBuffer: buffer length: 64]; + [hash updateWithBuffer: buffer length: length]; + } + + copy = [[hash copy] autorelease]; + + [hash calculate]; + [copy calculate]; + + OTAssertEqual(memcmp(hash.digest, expectedDigest, hash.digestSize), 0); + OTAssertEqual(memcmp(hash.digest, expectedDigest, hash.digestSize), 0); + + OTAssertThrowsSpecific([hash updateWithBuffer: "" length: 1], + OFHashAlreadyCalculatedException); +} + +- (void)testMD5 +{ + [self testHash: [OFMD5Hash class] expectedDigest: testFileMD5]; +} + +- (void)testRIPEMD160 +{ + [self testHash: [OFRIPEMD160Hash class] + expectedDigest: testFileRIPEMD160]; +} + +- (void)testSHA1 +{ + [self testHash: [OFSHA1Hash class] expectedDigest: testFileSHA1]; +} + +- (void)testSHA224 +{ + [self testHash: [OFSHA224Hash class] expectedDigest: testFileSHA224]; +} + +- (void)testSHA256 +{ + [self testHash: [OFSHA256Hash class] expectedDigest: testFileSHA256]; +} + +- (void)testSHA384 +{ + [self testHash: [OFSHA384Hash class] expectedDigest: testFileSHA384]; +} + +- (void)testSHA512 +{ + [self testHash: [OFSHA512Hash class] expectedDigest: testFileSHA512]; +} +@end Index: tests/OFDDPSocketTests.m ================================================================== --- tests/OFDDPSocketTests.m +++ tests/OFDDPSocketTests.m @@ -14,63 +14,50 @@ */ #include "config.h" #include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFDDPSocket"; - -@implementation TestsAppDelegate (OFDDPSocketTests) -- (void)DDPSocketTests -{ - void *pool = objc_autoreleasePoolPush(); +#include + +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFDDPSocketTests: OTTestCase +@end + +@implementation OFDDPSocketTests +- (void)testDDPSocket +{ OFDDPSocket *sock; OFSocketAddress address1, address2; char buffer[5]; - TEST(@"+[socket]", (sock = [OFDDPSocket socket])) + sock = [OFDDPSocket socket]; @try { - TEST(@"-[bindToNetwork:node:port:]", - R(address1 = [sock bindToNetwork: 0 - node: 0 - port: 0 - protocolType: 11])) + address1 = [sock bindToNetwork: 0 + node: 0 + port: 0 + protocolType: 11]; } @catch (OFBindSocketFailedException *e) { switch (e.errNo) { case EAFNOSUPPORT: case EPROTONOSUPPORT: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFDDPSocket] -[bindToNetwork:node:port:" - @"protocolType:] AppleTalk unsupported, skipping " - @"tests"]; - break; + OTSkip(@"AppleTalk unsupported"); case EADDRNOTAVAIL: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFDDPSocket] -[bindToNetwork:node:port:" - @"protocolType:] AppleTalk not configured, " - @"skipping tests"]; - break; + OTSkip(@"AppleTalk not configured"); default: @throw e; } - - objc_autoreleasePoolPop(pool); - return; - } - - TEST(@"-[sendBuffer:length:receiver:]", - R([sock sendBuffer: "Hello" length: 5 receiver: &address1])) - - TEST(@"-[receiveIntoBuffer:length:sender:]", - [sock receiveIntoBuffer: buffer length: 5 sender: &address2] == 5 && - memcmp(buffer, "Hello", 5) == 0 && - OFSocketAddressEqual(&address1, &address2) && - OFSocketAddressHash(&address1) == OFSocketAddressHash(&address2)) - - objc_autoreleasePoolPop(pool); + } + + [sock sendBuffer: "Hello" length: 5 receiver: &address1]; + + OTAssertEqual([sock receiveIntoBuffer: buffer + length: 5 + sender: &address2], 5); + OTAssertEqual(memcmp(buffer, "Hello", 5), 0); + OTAssertTrue(OFSocketAddressEqual(&address1, &address2)); + OTAssertEqual(OFSocketAddressHash(&address1), + OFSocketAddressHash(&address2)); } @end Index: tests/OFDNSResolverTests.m ================================================================== --- tests/OFDNSResolverTests.m +++ tests/OFDNSResolverTests.m @@ -13,20 +13,32 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFDNSResolverTests: OTTestCase +@end -@implementation TestsAppDelegate (OFDNSResolverTests) -- (void)DNSResolverTests +@implementation OFDNSResolverTests ++ (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, id) *) *)summary { - void *pool = objc_autoreleasePoolPush(); + OFMutableArray *summary = [OFMutableArray array]; OFDNSResolver *resolver = [OFDNSResolver resolver]; OFMutableString *staticHosts = [OFMutableString string]; - [OFStdOut setForegroundColor: [OFColor lime]]; +#define ADD(name, value) \ + [summary addObject: [OFPair pairWithFirstObject: name \ + secondObject: value]]; +#define ADD_DOUBLE(name, value) \ + ADD(name, [OFNumber numberWithDouble: value]) +#define ADD_UINT(name, value) \ + ADD(name, [OFNumber numberWithUnsignedInt: value]); +#define ADD_BOOL(name, value) \ + ADD(name, [OFNumber numberWithBool: value]); for (OFString *host in resolver.staticHosts) { OFString *IPs; if (staticHosts.length > 0) @@ -35,37 +47,28 @@ IPs = [[resolver.staticHosts objectForKey: host] componentsJoinedByString: @", "]; [staticHosts appendFormat: @"%@=(%@)", host, IPs]; } - [OFStdOut writeFormat: @"[OFDNSResolver] Static hosts: %@\n", - staticHosts]; - - [OFStdOut writeFormat: @"[OFDNSResolver] Name servers: %@\n", - [resolver.nameServers componentsJoinedByString: @", "]]; - - [OFStdOut writeFormat: @"[OFDNSResolver] Local domain: %@\n", - resolver.localDomain]; - - [OFStdOut writeFormat: @"[OFDNSResolver] Search domains: %@\n", - [resolver.searchDomains componentsJoinedByString: @", "]]; - - [OFStdOut writeFormat: @"[OFDNSResolver] Timeout: %lf\n", - resolver.timeout]; - - [OFStdOut writeFormat: @"[OFDNSResolver] Max attempts: %u\n", - resolver.maxAttempts]; - - [OFStdOut writeFormat: - @"[OFDNSResolver] Min number of dots in absolute name: %u\n", - resolver.minNumberOfDotsInAbsoluteName]; - - [OFStdOut writeFormat: @"[OFDNSResolver] Forces TCP: %u\n", - resolver.forcesTCP]; - - [OFStdOut writeFormat: - @"[OFDNSResolver] Config reload interval: %lf\n", - resolver.configReloadInterval]; - - objc_autoreleasePoolPop(pool); + ADD(@"Static hosts", staticHosts) + + ADD(@"Name servers", + [resolver.nameServers componentsJoinedByString: @", "]); + ADD(@"Local domain", resolver.localDomain); + ADD(@"Search domains", + [resolver.searchDomains componentsJoinedByString: @", "]); + + ADD_DOUBLE(@"Timeout", resolver.timeout); + ADD_UINT(@"Max attempts", resolver.maxAttempts); + ADD_UINT(@"Min number of dots in absolute name", + resolver.minNumberOfDotsInAbsoluteName); + ADD_BOOL(@"Forces TCP", resolver.forcesTCP); + ADD_DOUBLE(@"Config reload interval", resolver.configReloadInterval); + +#undef ADD +#undef ADD_DOUBLE +#undef ADD_UINT +#undef ADD_BOOL + + return summary; } @end ADDED tests/OFDataTests.h Index: tests/OFDataTests.h ================================================================== --- /dev/null +++ tests/OFDataTests.h @@ -0,0 +1,26 @@ +/* + * 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 "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFDataTests: OTTestCase +{ + OFData *_data; + unsigned char _items[2][4096]; +} + +@property (readonly, nonatomic) Class dataClass; +@end Index: tests/OFDataTests.m ================================================================== --- tests/OFDataTests.m +++ tests/OFDataTests.m @@ -15,214 +15,277 @@ #include "config.h" #include -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFData"; - -@implementation TestsAppDelegate (OFDataTests) -- (void)dataTests -{ - void *pool = objc_autoreleasePoolPush(); - OFMutableData *mutableData; - OFData *data; - void *raw[2]; - OFRange range; - - TEST(@"+[dataWithItemSize:]", - (mutableData = [OFMutableData dataWithItemSize: 4096])) - - raw[0] = OFAllocMemory(1, 4096); - raw[1] = OFAllocMemory(1, 4096); - memset(raw[0], 0xFF, 4096); - memset(raw[1], 0x42, 4096); - - TEST(@"-[addItem:]", R([mutableData addItem: raw[0]]) && - R([mutableData addItem: raw[1]])) - - TEST(@"-[itemAtIndex:]", - memcmp([mutableData itemAtIndex: 0], raw[0], 4096) == 0 && - memcmp([mutableData itemAtIndex: 1], raw[1], 4096) == 0) - - TEST(@"-[lastItem]", memcmp(mutableData.lastItem, raw[1], 4096) == 0) - - TEST(@"-[count]", mutableData.count == 2) - - TEST(@"-[isEqual:]", - (data = [OFData dataWithItems: mutableData.items - count: mutableData.count - itemSize: mutableData.itemSize]) && - [data isEqual: mutableData] && - R([mutableData removeLastItem]) && ![mutableData isEqual: data]) - - TEST(@"-[mutableCopy]", - (mutableData = [[data mutableCopy] autorelease]) && - [mutableData isEqual: data]) - - TEST(@"-[compare]", [mutableData compare: data] == 0 && - R([mutableData removeLastItem]) && - [data compare: mutableData] == OFOrderedDescending && - [mutableData compare: data] == OFOrderedAscending && - [[OFData dataWithItems: "aa" count: 2] compare: - [OFData dataWithItems: "z" count: 1]] == OFOrderedAscending) - - TEST(@"-[hash]", data.hash == 0x634A529F) - - mutableData = [OFMutableData dataWithItems: "abcdef" count: 6]; - - TEST(@"-[removeLastItem]", - R([mutableData removeLastItem]) && mutableData.count == 5 && - memcmp(mutableData.items, "abcde", 5) == 0) - - TEST(@"-[removeItemsInRange:]", - R([mutableData removeItemsInRange: OFMakeRange(1, 2)]) && - mutableData.count == 3 && memcmp(mutableData.items, "ade", 3) == 0) - - TEST(@"-[insertItems:atIndex:count:]", - R([mutableData insertItems: "bc" atIndex: 1 count: 2]) && - mutableData.count == 5 && - memcmp(mutableData.items, "abcde", 5) == 0) - - data = [OFData dataWithItems: "aaabaccdacaabb" count: 7 itemSize: 2]; - - range = [data rangeOfData: [OFData dataWithItems: "aa" - count: 1 - itemSize: 2] - options: 0 - range: OFMakeRange(0, 7)]; - TEST(@"-[rangeOfData:options:range:] #1", - range.location == 0 && range.length == 1) - - range = [data rangeOfData: [OFData dataWithItems: "aa" - count: 1 - itemSize: 2] - options: OFDataSearchBackwards - range: OFMakeRange(0, 7)]; - TEST(@"-[rangeOfData:options:range:] #2", - range.location == 5 && range.length == 1) - - range = [data rangeOfData: [OFData dataWithItems: "ac" - count: 1 - itemSize: 2] - options: 0 - range: OFMakeRange(0, 7)]; - TEST(@"-[rangeOfData:options:range:] #3", - range.location == 2 && range.length == 1) - - range = [data rangeOfData: [OFData dataWithItems: "aabb" - count: 2 - itemSize: 2] - options: 0 - range: OFMakeRange(0, 7)]; - TEST(@"-[rangeOfData:options:range:] #4", - range.location == 5 && range.length == 2) - - TEST(@"-[rangeOfData:options:range:] #5", - R(range = [data rangeOfData: [OFData dataWithItems: "aa" - count: 1 - itemSize: 2] - options: 0 - range: OFMakeRange(1, 6)]) && - range.location == 5 && range.length == 1) - - range = [data rangeOfData: [OFData dataWithItems: "aa" - count: 1 - itemSize: 2] - options: OFDataSearchBackwards - range: OFMakeRange(0, 5)]; - TEST(@"-[rangeOfData:options:range:] #6", - range.location == 0 && range.length == 1) - - EXPECT_EXCEPTION( - @"-[rangeOfData:options:range:] failing on different itemSize", - OFInvalidArgumentException, - [data rangeOfData: [OFData dataWithItems: "aaa" - count: 1 - itemSize: 3] - options: 0 - range: OFMakeRange(0, 1)]) - - EXPECT_EXCEPTION( - @"-[rangeOfData:options:range:] failing on out of range", - OFOutOfRangeException, - [data rangeOfData: [OFData dataWithItems: "" count: 0 itemSize: 2] - options: 0 - range: OFMakeRange(8, 1)]) - - TEST(@"-[subdataWithRange:]", - [[data subdataWithRange: OFMakeRange(2, 4)] - isEqual: [OFData dataWithItems: "accdacaa" count: 4 itemSize: 2]] && - [[mutableData subdataWithRange: OFMakeRange(2, 3)] - isEqual: [OFData dataWithItems: "cde" count: 3]]) - - EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #1", - OFOutOfRangeException, - [data subdataWithRange: OFMakeRange(7, 1)]) - - EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #2", - OFOutOfRangeException, - [mutableData subdataWithRange: OFMakeRange(6, 1)]) - - TEST(@"-[stringByMD5Hashing]", - [mutableData.stringByMD5Hashing - isEqual: @"ab56b4d92b40713acc5af89985d4b786"]) - - TEST(@"-[stringByRIPEMD160Hashing]", - [mutableData.stringByRIPEMD160Hashing - isEqual: @"973398b6e6c6cfa6b5e6a5173f195ce3274bf828"]) - - TEST(@"-[stringBySHA1Hashing]", - [mutableData.stringBySHA1Hashing - isEqual: @"03de6c570bfe24bfc328ccd7ca46b76eadaf4334"]) - - TEST(@"-[stringBySHA224Hashing]", - [mutableData.stringBySHA224Hashing - isEqual: @"bdd03d560993e675516ba5a50638b6531ac2ac3d5847c61916cfced6" - ]) - - TEST(@"-[stringBySHA256Hashing]", - [mutableData.stringBySHA256Hashing - isEqual: @"36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0" - @"c44ca42c"]) - - TEST(@"-[stringBySHA384Hashing]", - [mutableData.stringBySHA384Hashing - isEqual: @"4c525cbeac729eaf4b4665815bc5db0c84fe6300068a727cf74e2813" - @"521565abc0ec57a37ee4d8be89d097c0d2ad52f0"]) - - TEST(@"-[stringBySHA512Hashing]", - [mutableData.stringBySHA512Hashing - isEqual: @"878ae65a92e86cac011a570d4c30a7eaec442b85ce8eca0c2952b5e3" - @"cc0628c2e79d889ad4d5c7c626986d452dd86374b6ffaa7cd8b67665" - @"bef2289a5c70b0a1"]) - - TEST(@"-[stringByBase64Encoding]", - [mutableData.stringByBase64Encoding isEqual: @"YWJjZGU="]) - - TEST(@"+[dataWithBase64EncodedString:]", - memcmp([[OFData dataWithBase64EncodedString: @"YWJjZGU="] items], - "abcde", 5) == 0) - - TEST(@"Building strings", - (mutableData = [OFMutableData dataWithItems: "Hello!" count: 6]) && - R([mutableData addItem: ""]) && - strcmp(mutableData.items, "Hello!") == 0) - - EXPECT_EXCEPTION(@"Detect out of range in -[itemAtIndex:]", - OFOutOfRangeException, [mutableData itemAtIndex: mutableData.count]) - - EXPECT_EXCEPTION(@"Detect out of range in -[addItems:count:]", - OFOutOfRangeException, - [mutableData addItems: raw[0] count: SIZE_MAX]) - - EXPECT_EXCEPTION(@"Detect out of range in -[removeItemsInRange:]", - OFOutOfRangeException, - [mutableData removeItemsInRange: OFMakeRange(mutableData.count, 1)]) - - OFFreeMemory(raw[0]); - OFFreeMemory(raw[1]); - - objc_autoreleasePoolPop(pool); +#import "OFDataTests.h" + +@implementation OFDataTests +- (Class)dataClass +{ + return [OFData class]; +} + +- (void)setUp +{ + [super setUp]; + + memset(&_items[0], 0xFF, 4096); + memset(&_items[1], 0x42, 4096); + + _data = [[self.dataClass alloc] initWithItems: _items + count: 2 + itemSize: 4096]; +} + +- (void)dealloc +{ + [_data release]; + + [super dealloc]; +} + +- (void)testCount +{ + OTAssertEqual(_data.count, 2); +} + +- (void)testItemSize +{ + OTAssertEqual(_data.itemSize, 4096); +} + +- (void)testItems +{ + OTAssertEqual(memcmp(_data.items, _items, 2 * _data.itemSize), 0); +} + +- (void)testItemAtIndex +{ + OTAssertEqual( + memcmp([_data itemAtIndex: 1], &_items[1], _data.itemSize), 0); +} + +- (void)testItemAtIndexThrowsOnOutOfRangeIndex +{ + OTAssertThrowsSpecific([_data itemAtIndex: _data.count], + OFOutOfRangeException); +} + +- (void)testFirstItem +{ + OTAssertEqual(memcmp(_data.firstItem, &_items[0], _data.itemSize), 0); +} + +- (void)testLastItem +{ + OTAssertEqual(memcmp(_data.lastItem, &_items[1], _data.itemSize), 0); +} + +- (void)testIsEqual +{ + OTAssertEqualObjects( + _data, [OFData dataWithItems: _items count: 2 itemSize: 4096]); + OTAssertNotEqualObjects( + _data, [OFData dataWithItems: _items count: 1 itemSize: 4096]); +} + +- (void)testHash +{ + OTAssertEqual(_data.hash, + [[OFData dataWithItems: _items count: 2 itemSize: 4096] hash]); + OTAssertNotEqual(_data.hash, + [[OFData dataWithItems: _items count: 1 itemSize: 4096] hash]); +} + +- (void)testCompare +{ + OFData *data1 = [self.dataClass dataWithItems: "aa" count: 2]; + OFData *data2 = [self.dataClass dataWithItems: "ab" count: 2]; + OFData *data3 = [self.dataClass dataWithItems: "aaa" count: 3]; + + OTAssertEqual([data1 compare: data2], OFOrderedAscending); + OTAssertEqual([data2 compare: data1], OFOrderedDescending); + OTAssertEqual([data1 compare: data1], OFOrderedSame); + OTAssertEqual([data1 compare: data3], OFOrderedAscending); + OTAssertEqual([data2 compare: data3], OFOrderedDescending); +} + +- (void)testCopy +{ + OTAssertEqualObjects([[_data copy] autorelease], _data); +} + +- (void)testRangeOfDataOptionsRange +{ + OFData *data = [self.dataClass dataWithItems: "aaabaccdacaabb" + count: 7 + itemSize: 2]; + OFRange range; + + range = [data rangeOfData: [self.dataClass dataWithItems: "aa" + count: 1 + itemSize: 2] + options: 0 + range: OFMakeRange(0, 7)]; + OTAssertEqual(range.location, 0); + OTAssertEqual(range.length, 1); + + range = [data rangeOfData: [self.dataClass dataWithItems: "aa" + count: 1 + itemSize: 2] + options: OFDataSearchBackwards + range: OFMakeRange(0, 7)]; + OTAssertEqual(range.location, 5); + OTAssertEqual(range.length, 1); + + range = [data rangeOfData: [self.dataClass dataWithItems: "ac" + count: 1 + itemSize: 2] + options: 0 + range: OFMakeRange(0, 7)]; + OTAssertEqual(range.location, 2); + OTAssertEqual(range.length, 1); + + range = [data rangeOfData: [self.dataClass dataWithItems: "aabb" + count: 2 + itemSize: 2] + options: 0 + range: OFMakeRange(0, 7)]; + OTAssertEqual(range.location, 5); + OTAssertEqual(range.length, 2); + + range = [data rangeOfData: [self.dataClass dataWithItems: "aa" + count: 1 + itemSize: 2] + options: 0 + range: OFMakeRange(1, 6)]; + OTAssertEqual(range.location, 5); + OTAssertEqual(range.length, 1); + + range = [data rangeOfData: [self.dataClass dataWithItems: "aa" + count: 1 + itemSize: 2] + options: OFDataSearchBackwards + range: OFMakeRange(0, 5)]; + OTAssertEqual(range.location, 0); + OTAssertEqual(range.length, 1); +} + +- (void)testRangeOfDataOptionsRangeThrowsOnDifferentItemSize +{ + OTAssertThrowsSpecific( + [_data rangeOfData: [OFData dataWithItems: "a" count: 1] + options: 0 + range: OFMakeRange(0, 1)], + OFInvalidArgumentException); +} + +- (void)testRangeOfDataOptionsRangeThrowsOnOutOfRangeRange +{ + OTAssertThrowsSpecific( + [_data rangeOfData: [OFData dataWithItemSize: 4096] + options: 0 + range: OFMakeRange(1, 2)], + OFOutOfRangeException); + + OTAssertThrowsSpecific( + [_data rangeOfData: [OFData dataWithItemSize: 4096] + options: 0 + range: OFMakeRange(2, 1)], + OFOutOfRangeException); +} + +- (void)testSubdataWithRange +{ + OFData *data1 = [self.dataClass dataWithItems: "aaabaccdacaabb" + count: 7 + itemSize: 2]; + OFData *data2 = [self.dataClass dataWithItems: "abcde" count: 5]; + + OTAssertEqualObjects( + [data1 subdataWithRange: OFMakeRange(2, 4)], + [OFData dataWithItems: "accdacaa" count: 4 itemSize: 2]); + + OTAssertEqualObjects( + [data2 subdataWithRange: OFMakeRange(2, 3)], + [OFData dataWithItems: "cde" count: 3]); +} + +- (void)testSubdataWithRangeThrowsOnOutOfRangeRange +{ + OFData *data1 = [self.dataClass dataWithItems: "aaabaccdacaabb" + count: 7 + itemSize: 2]; + OFData *data2 = [self.dataClass dataWithItems: "abcde" count: 5]; + + OTAssertThrowsSpecific([data1 subdataWithRange: OFMakeRange(7, 1)], + OFOutOfRangeException); + + OTAssertThrowsSpecific([data1 subdataWithRange: OFMakeRange(8, 0)], + OFOutOfRangeException); + + OTAssertThrowsSpecific([data2 subdataWithRange: OFMakeRange(6, 1)], + OFOutOfRangeException); +} + +- (void)testStringByMD5Hashing +{ + OTAssertEqualObjects(_data.stringByMD5Hashing, + @"37d65c8816008d58175b1d71ee892de3"); +} + +- (void)testStringByRIPEMD160Hashing +{ + OTAssertEqualObjects(_data.stringByRIPEMD160Hashing, + @"ab33a6a725f9fcec6299054dc604c0eb650cd889"); +} + +- (void)testStringBySHA1Hashing +{ + OTAssertEqualObjects(_data.stringBySHA1Hashing, + @"eb50cfcc29d0bed96b3bafe03e99110bcf6663b3"); +} + +- (void)testStringBySHA224Hashing +{ + OTAssertEqualObjects(_data.stringBySHA224Hashing, + @"204f8418a914a6828f8eb27871e01f74366f6d8fac8936029ebf0041"); +} + +- (void)testStringBySHA256Hashing +{ + OTAssertEqualObjects(_data.stringBySHA256Hashing, + @"27c521859f6f5b10aeac4e210a6d005c" + @"85e382c594e2622af9c46c6da8906821"); +} + +- (void)testStringBySHA384Hashing +{ + OTAssertEqualObjects(_data.stringBySHA384Hashing, + @"af99a52c26c00f01fe649dcc53d7c7a0" + @"a9ee0150b971955be2af395708966120" + @"5f2634f70df083ef63b232d5b8549db4"); +} + +- (void)testStringBySHA512Hashing +{ + OTAssertEqualObjects(_data.stringBySHA512Hashing, + @"1cbd53bf8bed9b45a63edda645ee1217" + @"24d2f0323c865e1039ba13320bc6c66e" + @"c79b6cdf6d08395c612b7decb1e59ad1" + @"e72bfa007c2f76a823d10204d47d2e2d"); +} + +- (void)testStringByBase64Encoding +{ + OTAssertEqualObjects([[self.dataClass dataWithItems: "abcde" count: 5] + stringByBase64Encoding], @"YWJjZGU="); +} + +- (void)testDataWithBase64EncodedString +{ + OTAssertEqualObjects( + [self.dataClass dataWithBase64EncodedString: @"YWJjZGU="], + [OFData dataWithItems: "abcde" count: 5]); } @end Index: tests/OFDateTests.m ================================================================== --- tests/OFDateTests.m +++ tests/OFDateTests.m @@ -15,98 +15,184 @@ #include "config.h" #include -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" + #import "OFStrPTime.h" -static OFString *const module = @"OFDate"; - -@implementation TestsAppDelegate (OFDateTests) -- (void)dateTests -{ - void *pool = objc_autoreleasePoolPush(); - OFDate *date1, *date2; - +@interface OFDateTests: OTTestCase +{ + OFDate *_date[2]; +} +@end + +@implementation OFDateTests +- (void)setUp +{ + [super setUp]; + + _date[0] = [[OFDate alloc] initWithTimeIntervalSince1970: 0]; + _date[1] = [[OFDate alloc] + initWithTimeIntervalSince1970: 3600 * 25 + 5.000002]; +} + +- (void)dealloc +{ + [_date[0] release]; + [_date[1] release]; + + [super dealloc]; +} + +- (void)testStrPTime +{ struct tm tm; - int16_t tz; - const char *dstr = "Wed, 09 Jun 2021 +0200x"; - TEST(@"OFStrPTime()", - OFStrPTime(dstr, "%a, %d %b %Y %z", &tm, &tz) == dstr + 22 && - tm.tm_wday == 3 && tm.tm_mday == 9 && tm.tm_mon == 5 && - tm.tm_year == 2021 - 1900 && tz == 2 * 60) - - TEST(@"+[dateWithTimeIntervalSince1970:]", - (date1 = [OFDate dateWithTimeIntervalSince1970: 0])) - - TEST(@"-[dateByAddingTimeInterval:]", - (date2 = [date1 dateByAddingTimeInterval: 3600 * 25 + 5.000002])) - - TEST(@"-[description]", - [date1.description isEqual: @"1970-01-01T00:00:00Z"] && - [date2.description isEqual: @"1970-01-02T01:00:05Z"]) - - TEST(@"+[dateWithDateString:format:]", - [[[OFDate dateWithDateString: @"2000-06-20T12:34:56+0200" - format: @"%Y-%m-%dT%H:%M:%S%z"] description] - isEqual: @"2000-06-20T10:34:56Z"]); - - EXPECT_EXCEPTION(@"Detection of unparsed in " - @"+[dateWithDateString:format:]", OFInvalidFormatException, + int16_t timeZone; + const char *dateString = "Wed, 09 Jun 2021 +0200x"; + + OTAssertEqual(OFStrPTime(dateString, "%a, %d %b %Y %z", &tm, &timeZone), + dateString + 22); + OTAssertEqual(tm.tm_wday, 3); + OTAssertEqual(tm.tm_mday, 9); + OTAssertEqual(tm.tm_mon, 5); + OTAssertEqual(tm.tm_year, 2021 - 1900); + OTAssertEqual(timeZone, 2 * 60); +} + +- (void)testDateByAddingTimeInterval +{ + OTAssertEqualObjects( + [_date[0] dateByAddingTimeInterval: 3600 * 25 + 5.000002], + _date[1]); +} + +- (void)testDescription +{ + OTAssertEqualObjects(_date[0].description, @"1970-01-01T00:00:00Z"); + OTAssertEqualObjects(_date[1].description, @"1970-01-02T01:00:05Z"); +} + +- (void)testDateWithDateStringFormat +{ + OTAssertEqualObjects( + [[OFDate dateWithDateString: @"2000-06-20T12:34:56+0200" + format: @"%Y-%m-%dT%H:%M:%S%z"] description], + @"2000-06-20T10:34:56Z"); +} + +- (void)testDateWithDateStringFormatFailsWithTrailingCharacters +{ + OTAssertThrowsSpecific( [OFDate dateWithDateString: @"2000-06-20T12:34:56+0200x" - format: @"%Y-%m-%dT%H:%M:%S%z"]) - - TEST(@"+[dateWithLocalDateString:format:]", - [[[OFDate dateWithLocalDateString: @"2000-06-20T12:34:56" - format: @"%Y-%m-%dT%H:%M:%S"] - localDateStringWithFormat: @"%Y-%m-%dT%H:%M:%S"] - isEqual: @"2000-06-20T12:34:56"]); - - TEST(@"+[dateWithLocalDateString:format:]", - [[[OFDate dateWithLocalDateString: @"2000-06-20T12:34:56-0200" - format: @"%Y-%m-%dT%H:%M:%S%z"] - description] isEqual: @"2000-06-20T14:34:56Z"]); - - EXPECT_EXCEPTION(@"Detection of unparsed in " - @"+[dateWithLocalDateString:format:] #1", OFInvalidFormatException, + format: @"%Y-%m-%dT%H:%M:%S%z"], + OFInvalidFormatException); +} + +- (void)testDateWithLocalDateStringFormatFormat +{ + OTAssertEqualObjects( + [[OFDate dateWithLocalDateString: @"2000-06-20T12:34:56" + format: @"%Y-%m-%dT%H:%M:%S"] + localDateStringWithFormat: @"%Y-%m-%dT%H:%M:%S"], + @"2000-06-20T12:34:56"); + + OTAssertEqualObjects( + [[OFDate dateWithLocalDateString: @"2000-06-20T12:34:56-0200" + format: @"%Y-%m-%dT%H:%M:%S%z"] + description], + @"2000-06-20T14:34:56Z"); +} + +- (void)testDateWithLocalDateStringFormatFailsWithTrailingCharacters +{ + OTAssertThrowsSpecific( [OFDate dateWithLocalDateString: @"2000-06-20T12:34:56x" - format: @"%Y-%m-%dT%H:%M:%S"]) + format: @"%Y-%m-%dT%H:%M:%S"], + OFInvalidFormatException); - EXPECT_EXCEPTION(@"Detection of unparsed in " - @"+[dateWithLocalDateString:format:] #2", OFInvalidFormatException, + OTAssertThrowsSpecific( [OFDate dateWithLocalDateString: @"2000-06-20T12:34:56+0200x" - format: @"%Y-%m-%dT%H:%M:%S%z"]) - - TEST(@"-[isEqual:]", - [date1 isEqual: [OFDate dateWithTimeIntervalSince1970: 0]] && - ![date1 isEqual: [OFDate dateWithTimeIntervalSince1970: 0.0000001]]) - - TEST(@"-[compare:]", [date1 compare: date2] == OFOrderedAscending) - - TEST(@"-[second]", date1.second == 0 && date2.second == 5) - - TEST(@"-[microsecond]", - date1.microsecond == 0 && date2.microsecond == 2) - - TEST(@"-[minute]", date1.minute == 0 && date2.minute == 0) - - TEST(@"-[hour]", date1.hour == 0 && date2.hour == 1) - - TEST(@"-[dayOfMonth]", date1.dayOfMonth == 1 && date2.dayOfMonth == 2) - - TEST(@"-[monthOfYear]", - date1.monthOfYear == 1 && date2.monthOfYear == 1) - - TEST(@"-[year]", date1.year == 1970 && date2.year == 1970) - - TEST(@"-[dayOfWeek]", date1.dayOfWeek == 4 && date2.dayOfWeek == 5) - - TEST(@"-[dayOfYear]", date1.dayOfYear == 1 && date2.dayOfYear == 2) - - TEST(@"-[earlierDate:]", [[date1 earlierDate: date2] isEqual: date1]) - - TEST(@"-[laterDate:]", [[date1 laterDate: date2] isEqual: date2]) - - objc_autoreleasePoolPop(pool); + format: @"%Y-%m-%dT%H:%M:%S%z"], + OFInvalidFormatException); +} + +- (void)testIsEqual +{ + OTAssertEqualObjects(_date[0], + [OFDate dateWithTimeIntervalSince1970: 0]); + + OTAssertNotEqualObjects(_date[0], + [OFDate dateWithTimeIntervalSince1970: 0.0000001]); +} + +- (void)testCompare +{ + OTAssertEqual([_date[0] compare: _date[1]], OFOrderedAscending); +} + +- (void)testSecond +{ + OTAssertEqual(_date[0].second, 0); + OTAssertEqual(_date[1].second, 5); +} + +- (void)testMicrosecond +{ + OTAssertEqual(_date[0].microsecond, 0); + OTAssertEqual(_date[1].microsecond, 2); +} + +- (void)testMinute +{ + OTAssertEqual(_date[0].minute, 0); + OTAssertEqual(_date[1].minute, 0); +} + +- (void)testHour +{ + OTAssertEqual(_date[0].hour, 0); + OTAssertEqual(_date[1].hour, 1); +} + +- (void)testDayOfMonth +{ + OTAssertEqual(_date[0].dayOfMonth, 1); + OTAssertEqual(_date[1].dayOfMonth, 2); +} + +- (void)testMonthOfYear +{ + OTAssertEqual(_date[0].monthOfYear, 1); + OTAssertEqual(_date[1].monthOfYear, 1); +} + +- (void)testYear +{ + OTAssertEqual(_date[0].year, 1970); + OTAssertEqual(_date[1].year, 1970); +} + +- (void)testDayOfWeek +{ + OTAssertEqual(_date[0].dayOfWeek, 4); + OTAssertEqual(_date[1].dayOfWeek, 5); +} + +- (void)testDayOfYear +{ + OTAssertEqual(_date[0].dayOfYear, 1); + OTAssertEqual(_date[1].dayOfYear, 2); +} + +- (void)testEarlierDate +{ + OTAssertEqualObjects([_date[0] earlierDate: _date[1]], _date[0]); +} + +- (void)testLaterDate +{ + OTAssertEqualObjects([_date[0] laterDate: _date[1]], _date[1]); } @end ADDED tests/OFDictionaryTests.h Index: tests/OFDictionaryTests.h ================================================================== --- /dev/null +++ tests/OFDictionaryTests.h @@ -0,0 +1,25 @@ +/* + * 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 "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFDictionaryTests: OTTestCase +{ + OFDictionary *_dictionary; +} + +@property (readonly, nonatomic) Class dictionaryClass; +@end Index: tests/OFDictionaryTests.m ================================================================== --- tests/OFDictionaryTests.m +++ tests/OFDictionaryTests.m @@ -13,77 +13,258 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "OFDictionaryTests.h" -static OFString *module; static OFString *keys[] = { @"key1", @"key2" }; -static OFString *values[] = { +static OFString *objects[] = { @"value1", @"value2" }; -@interface SimpleDictionary: OFDictionary -{ - OFMutableDictionary *_dictionary; -} -@end - -@interface SimpleMutableDictionary: OFMutableDictionary -{ - OFMutableDictionary *_dictionary; - unsigned long _mutations; -} -@end - -@implementation SimpleDictionary -- (instancetype)init -{ - self = [super init]; - - @try { - _dictionary = [[OFMutableDictionary alloc] init]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (instancetype)initWithKey: (id)key arguments: (va_list)arguments -{ - self = [super init]; - - @try { - _dictionary = [[OFMutableDictionary alloc] - initWithKey: key - arguments: arguments]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (instancetype)initWithObjects: (const id *)objects +@interface CustomDictionary: OFDictionary +{ + OFDictionary *_dictionary; +} +@end + +@implementation OFDictionaryTests +- (Class)dictionaryClass +{ + return [CustomDictionary class]; +} + +- (void)setUp +{ + [super setUp]; + + _dictionary = [[self.dictionaryClass alloc] initWithObjects: objects + forKeys: keys + count: 2]; +} + +- (void)dealloc +{ + [_dictionary release]; + + [super dealloc]; +} + +- (void)testObjectForKey +{ + OTAssertEqualObjects([_dictionary objectForKey: keys[0]], objects[0]); + OTAssertEqualObjects([_dictionary objectForKey: keys[1]], objects[1]); +} + +- (void)testCount +{ + OTAssertEqual(_dictionary.count, 2); +} + +- (void)testIsEqual +{ + OTAssertEqualObjects(_dictionary, + [OFDictionary dictionaryWithObjects: objects + forKeys: keys + count: 2]); + OTAssertNotEqualObjects(_dictionary, + [OFDictionary dictionaryWithObjects: keys + forKeys: objects + count: 2]); +} + +- (void)testHash +{ + OTAssertEqual(_dictionary.hash, + [[OFDictionary dictionaryWithObjects: objects + forKeys: keys + count: 2] hash]); + OTAssertNotEqual(_dictionary.hash, + [[OFDictionary dictionaryWithObject: objects[0] + forKey: keys[0]] hash]); +} + +- (void)testCopy +{ + OTAssertEqualObjects([[_dictionary copy] autorelease], _dictionary); +} + +- (void)testValueForKey +{ + OTAssertEqualObjects([_dictionary valueForKey: keys[0]], objects[0]); + OTAssertEqualObjects([_dictionary valueForKey: keys[1]], objects[1]); + OTAssertEqualObjects( + [_dictionary valueForKey: @"@count"], [OFNumber numberWithInt: 2]); +} + +- (void)testSetValueForKey +{ + OTAssertThrowsSpecific([_dictionary setValue: @"x" forKey: @"x"], + OFUndefinedKeyException); +} + +- (void)testContainsObject +{ + OTAssertTrue([_dictionary containsObject: objects[0]]); + OTAssertFalse([_dictionary containsObject: @"nonexistent"]); +} + +- (void)testContainsObjectIdenticalTo +{ + OTAssertTrue([_dictionary containsObjectIdenticalTo: objects[0]]); + OTAssertFalse([_dictionary containsObjectIdenticalTo: + [[objects[0] mutableCopy] autorelease]]); +} + +- (void)testDescription +{ + OTAssert( + [_dictionary.description isEqual: + @"{\n\tkey1 = value1;\n\tkey2 = value2;\n}"] || + [_dictionary.description isEqual: + @"{\n\tkey2 = value2;\n\tkey1 = value1;\n}"]); +} + +- (void)testAllKeys +{ + OTAssert( + [_dictionary.allKeys isEqual: + ([OFArray arrayWithObjects: keys[0], keys[1], nil])] || + [_dictionary.allKeys isEqual: + ([OFArray arrayWithObjects: keys[1], keys[0], nil])]); +} + +- (void)testAllObjects +{ + OTAssert( + [_dictionary.allObjects isEqual: + ([OFArray arrayWithObjects: objects[0], objects[1], nil])] || + [_dictionary.allObjects isEqual: + ([OFArray arrayWithObjects: objects[1], objects[0], nil])]); +} + +- (void)testKeyEnumerator +{ + OFEnumerator *enumerator = [_dictionary keyEnumerator]; + OFString *first, *second; + + first = [enumerator nextObject]; + second = [enumerator nextObject]; + OTAssertNil([enumerator nextObject]); + + OTAssert( + ([first isEqual: keys[0]] && [second isEqual: keys[1]]) || + ([first isEqual: keys[1]] && [second isEqual: keys[0]])); +} + +- (void)testObjectEnumerator +{ + OFEnumerator *enumerator = [_dictionary objectEnumerator]; + OFString *first, *second; + + first = [enumerator nextObject]; + second = [enumerator nextObject]; + OTAssertNil([enumerator nextObject]); + + OTAssert( + ([first isEqual: objects[0]] && [second isEqual: objects[1]]) || + ([first isEqual: objects[1]] && [second isEqual: objects[0]])); +} + +- (void)testFastEnumeration +{ + size_t i = 0; + OFString *first = nil, *second = nil; + + for (OFString *key in _dictionary) { + OTAssertLessThan(i, 2); + + switch (i++) { + case 0: + first = key; + break; + case 1: + second = key; + break; + } + } + + OTAssertEqual(i, 2); + OTAssert( + ([first isEqual: keys[0]] && [second isEqual: keys[1]]) || + ([first isEqual: keys[1]] && [second isEqual: keys[0]])); +} + +#ifdef OF_HAVE_BLOCKS +- (void)testEnumerateKeysAndObjectsUsingBlock +{ + __block size_t i = 0; + __block OFString *first = nil, *second = nil; + + [_dictionary enumerateKeysAndObjectsUsingBlock: + ^ (id key, id object, bool *stop) { + OTAssertLessThan(i, 2); + + switch (i++) { + case 0: + first = key; + break; + case 1: + second = key; + break; + } + }]; + + OTAssertEqual(i, 2); + OTAssert( + ([first isEqual: keys[0]] && [second isEqual: keys[1]]) || + ([first isEqual: keys[1]] && [second isEqual: keys[0]])); +} + +- (void)testMappedDictionaryUsingBlock +{ + OTAssertEqualObjects([_dictionary mappedDictionaryUsingBlock: + ^ id (id key, id object) { + if ([key isEqual: keys[0]]) + return @"val1"; + if ([key isEqual: keys[1]]) + return @"val2"; + + return nil; + }], + ([OFDictionary dictionaryWithKeysAndObjects: + @"key1", @"val1", @"key2", @"val2", nil])); +} + +- (void)testFilteredDictionaryUsingBlock +{ + OTAssertEqualObjects([_dictionary filteredDictionaryUsingBlock: + ^ bool (id key, id object) { + return [key isEqual: keys[0]]; + }], + [OFDictionary dictionaryWithObject: objects[0] + forKey: keys[0]]); +} +#endif +@end + +@implementation CustomDictionary +- (instancetype)initWithObjects: (const id *)objects_ forKeys: (const id *)keys_ count: (size_t)count { self = [super init]; @try { - _dictionary = [[OFMutableDictionary alloc] - initWithObjects: objects - forKeys: keys_ - count: count]; + _dictionary = [[OFDictionary alloc] initWithObjects: objects_ + forKeys: keys_ + count: count]; } @catch (id e) { [self release]; @throw e; } @@ -109,272 +290,6 @@ - (OFEnumerator *)keyEnumerator { return [_dictionary keyEnumerator]; } -@end - -@implementation SimpleMutableDictionary -+ (void)initialize -{ - if (self == [SimpleMutableDictionary class]) - [self inheritMethodsFromClass: [SimpleDictionary class]]; -} - -- (void)setObject: (id)object forKey: (id)key -{ - bool existed = ([_dictionary objectForKey: key] == nil); - - [_dictionary setObject: object forKey: key]; - - if (existed) - _mutations++; -} - -- (void)removeObjectForKey: (id)key -{ - bool existed = ([_dictionary objectForKey: key] == nil); - - [_dictionary removeObjectForKey: key]; - - if (existed) - _mutations++; -} - -- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state - objects: (id *)objects - count: (int)count -{ - int ret = [super countByEnumeratingWithState: state - objects: objects - count: count]; - - state->mutationsPtr = &_mutations; - - return ret; -} -@end - -@implementation TestsAppDelegate (OFDictionaryTests) -- (void)dictionaryTestsWithClass: (Class)dictionaryClass - mutableClass: (Class)mutableDictionaryClass -{ - void *pool = objc_autoreleasePoolPush(); - OFMutableDictionary *mutableDict = [mutableDictionaryClass dictionary]; - OFDictionary *dict; - OFEnumerator *keyEnumerator, *objectEnumerator; - OFArray *keysArray, *valuesArray; - - [mutableDict setObject: values[0] forKey: keys[0]]; - [mutableDict setValue: values[1] forKey: keys[1]]; - - TEST(@"-[objectForKey:]", - [[mutableDict objectForKey: keys[0]] isEqual: values[0]] && - [[mutableDict objectForKey: keys[1]] isEqual: values[1]] && - [mutableDict objectForKey: @"key3"] == nil) - - TEST(@"-[valueForKey:]", - [[mutableDict valueForKey: keys[0]] isEqual: values[0]] && - [[mutableDict valueForKey: @"@count"] isEqual: - [OFNumber numberWithInt: 2]]) - - EXPECT_EXCEPTION(@"Catching -[setValue:forKey:] on immutable " - @"dictionary", OFUndefinedKeyException, - [[dictionaryClass dictionary] setValue: @"x" forKey: @"x"]) - - TEST(@"-[containsObject:]", - [mutableDict containsObject: values[0]] && - ![mutableDict containsObject: @"nonexistent"]) - - TEST(@"-[containsObjectIdenticalTo:]", - [mutableDict containsObjectIdenticalTo: values[0]] && - ![mutableDict containsObjectIdenticalTo: - [OFString stringWithString: values[0]]]) - - TEST(@"-[description]", - [[mutableDict description] isEqual: - @"{\n\tkey1 = value1;\n\tkey2 = value2;\n}"]) - - TEST(@"-[allKeys]", - [[mutableDict allKeys] isEqual: - [OFArray arrayWithObjects: keys[0], keys[1], nil]]) - - TEST(@"-[allObjects]", - [[mutableDict allObjects] isEqual: - [OFArray arrayWithObjects: values[0], values[1], nil]]) - - TEST(@"-[keyEnumerator]", (keyEnumerator = [mutableDict keyEnumerator])) - TEST(@"-[objectEnumerator]", - (objectEnumerator = [mutableDict objectEnumerator])) - - TEST(@"OFEnumerator's -[nextObject]", - [[keyEnumerator nextObject] isEqual: keys[0]] && - [[objectEnumerator nextObject] isEqual: values[0]] && - [[keyEnumerator nextObject] isEqual: keys[1]] && - [[objectEnumerator nextObject] isEqual: values[1]] && - [keyEnumerator nextObject] == nil && - [objectEnumerator nextObject] == nil) - - [mutableDict removeObjectForKey: keys[0]]; - - EXPECT_EXCEPTION(@"Detection of mutation during enumeration", - OFEnumerationMutationException, [keyEnumerator nextObject]); - - [mutableDict setObject: values[0] forKey: keys[0]]; - - size_t i = 0; - bool ok = true; - - for (OFString *key in mutableDict) { - if (i > 1 || ![key isEqual: keys[i]]) { - ok = false; - break; - } - - [mutableDict setObject: [mutableDict objectForKey: key] - forKey: key]; - i++; - } - - TEST(@"Fast Enumeration", ok) - - ok = false; - @try { - for (OFString *key in mutableDict) { - (void)key; - [mutableDict setObject: @"" forKey: @""]; - } - } @catch (OFEnumerationMutationException *e) { - ok = true; - } - - TEST(@"Detection of mutation during Fast Enumeration", ok) - - [mutableDict removeObjectForKey: @""]; - -#ifdef OF_HAVE_BLOCKS - { - __block size_t j = 0; - __block bool blockOk = true; - - [mutableDict enumerateKeysAndObjectsUsingBlock: - ^ (id key, id object, bool *stop) { - if (j > 1 || ![key isEqual: keys[j]]) { - blockOk = false; - *stop = true; - return; - } - - [mutableDict setObject: [mutableDict objectForKey: key] - forKey: key]; - j++; - }]; - - TEST(@"Enumeration using blocks", blockOk) - - blockOk = false; - @try { - [mutableDict enumerateKeysAndObjectsUsingBlock: - ^ (id key, id object, bool *stop) { - [mutableDict setObject: @"" forKey: @""]; - }]; - } @catch (OFEnumerationMutationException *e) { - blockOk = true; - } - - TEST(@"Detection of mutation during enumeration using blocks", - blockOk) - - [mutableDict removeObjectForKey: @""]; - } - - TEST(@"-[replaceObjectsUsingBlock:]", - R([mutableDict replaceObjectsUsingBlock: ^ id (id key, id object) { - if ([key isEqual: keys[0]]) - return @"value_1"; - if ([key isEqual: keys[1]]) - return @"value_2"; - - return nil; - }]) && [[mutableDict objectForKey: keys[0]] isEqual: @"value_1"] && - [[mutableDict objectForKey: keys[1]] isEqual: @"value_2"]) - - TEST(@"-[mappedDictionaryUsingBlock:]", - [[[mutableDict mappedDictionaryUsingBlock: - ^ id (id key, id object) { - if ([key isEqual: keys[0]]) - return @"val1"; - if ([key isEqual: keys[1]]) - return @"val2"; - - return nil; - }] description] isEqual: @"{\n\tkey1 = val1;\n\tkey2 = val2;\n}"]) - - TEST(@"-[filteredDictionaryUsingBlock:]", - [[[mutableDict filteredDictionaryUsingBlock: - ^ bool (id key, id object) { - return [key isEqual: keys[0]]; - }] description] isEqual: @"{\n\tkey1 = value_1;\n}"]) -#endif - - TEST(@"-[count]", mutableDict.count == 2) - - TEST(@"+[dictionaryWithKeysAndObjects:]", - (dict = [dictionaryClass dictionaryWithKeysAndObjects: - @"foo", @"bar", @"baz", @"qux", nil]) && - [[dict objectForKey: @"foo"] isEqual: @"bar"] && - [[dict objectForKey: @"baz"] isEqual: @"qux"]) - - TEST(@"+[dictionaryWithObject:forKey:]", - (dict = [dictionaryClass dictionaryWithObject: @"bar" - forKey: @"foo"]) && - [[dict objectForKey: @"foo"] isEqual: @"bar"]) - - keysArray = [OFArray arrayWithObjects: keys[0], keys[1], nil]; - valuesArray = [OFArray arrayWithObjects: values[0], values[1], nil]; - TEST(@"+[dictionaryWithObjects:forKeys:]", - (dict = [dictionaryClass dictionaryWithObjects: valuesArray - forKeys: keysArray]) && - [[dict objectForKey: keys[0]] isEqual: values[0]] && - [[dict objectForKey: keys[1]] isEqual: values[1]]) - - TEST(@"-[copy]", - (dict = [[dict copy] autorelease]) && - [[dict objectForKey: keys[0]] isEqual: values[0]] && - [[dict objectForKey: keys[1]] isEqual: values[1]]) - - TEST(@"-[mutableCopy]", - (mutableDict = [[dict mutableCopy] autorelease]) && - mutableDict.count == dict.count && - [[mutableDict objectForKey: keys[0]] isEqual: values[0]] && - [[mutableDict objectForKey: keys[1]] isEqual: values[1]] && - R([mutableDict setObject: @"value3" forKey: @"key3"]) && - [[mutableDict objectForKey: @"key3"] isEqual: @"value3"] && - [[mutableDict objectForKey: keys[0]] isEqual: values[0]] && - R([mutableDict setObject: @"foo" forKey: keys[0]]) && - [[mutableDict objectForKey: keys[0]] isEqual: @"foo"]) - - TEST(@"-[removeObjectForKey:]", - R([mutableDict removeObjectForKey: keys[0]]) && - [mutableDict objectForKey: keys[0]] == nil) - - [mutableDict setObject: @"foo" forKey: keys[0]]; - TEST(@"-[isEqual:]", ![mutableDict isEqual: dict] && - R([mutableDict removeObjectForKey: @"key3"]) && - ![mutableDict isEqual: dict] && - R([mutableDict setObject: values[0] forKey: keys[0]]) && - [mutableDict isEqual: dict]) - - objc_autoreleasePoolPop(pool); -} - -- (void)dictionaryTests -{ - module = @"OFDictionary"; - [self dictionaryTestsWithClass: [SimpleDictionary class] - mutableClass: [SimpleMutableDictionary class]]; - - module = @"OFDictionary_hashtable"; - [self dictionaryTestsWithClass: [OFDictionary class] - mutableClass: [OFMutableDictionary class]]; -} @end Index: tests/OFHMACTests.m ================================================================== --- tests/OFHMACTests.m +++ tests/OFHMACTests.m @@ -15,13 +15,19 @@ #include "config.h" #include -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFHMAC"; +@interface OFHMACTests: OTTestCase +{ + OFStream *_stream; +} +@end + static const uint8_t key[] = "yM9h8K6IWnJRvxC/0F8XRWG7RnACDBz8wqK2tbXrYVLoKC3vPLeJikyJSM47tVHc" "DlXHww9zULAC2sJUlm2Kg1z4oz2aXY3Y1PQSB4VkC/m0DQ7hCI6cAg4TWnKdzWTy" "cvYGX+Y6HWeDY79/PGSd8fNItme6I8w4HDBqU7BP2sum3jbePJqoiSnhcyJZQTeZ" "jw0ZXoyrfHgOYD2M+NsTDaGpLblFtQ7n5CczjKtafG40PkEwx1dcrd46U9i3GyTK"; @@ -45,86 +51,88 @@ "\xF5\x8C\x3F\x9C\xA2\x2F\x0A\xF3\x26\xD8\xC0\x7E\x20\x63\x88\x61" "\xC9\xE1\x1F\xD7\xC7\xE5\x59\x33\xD5\x2F\xAF\x56\x1C\x94\xC8\xA4" "\x61\xB3\xF9\x1A\xE3\x09\x43\xA6\x5B\x85\xB1\x50\x5B\xCB\x1A\x2E" "\xB7\xE8\x87\xC1\x73\x19\x63\xF6\xA2\x91\x8D\x7E\x2E\xCC\xEC\x99"; -@implementation TestsAppDelegate (OFHMACTests) -- (void)HMACTests -{ - void *pool = objc_autoreleasePoolPush(); - OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; - OFHMAC *HMACMD5, *HMACSHA1, *HMACRMD160; - OFHMAC *HMACSHA256, *HMACSHA384, *HMACSHA512; - - TEST(@"+[HMACWithHashClass:] with MD5", - (HMACMD5 = [OFHMAC HMACWithHashClass: [OFMD5Hash class] - allowsSwappableMemory: true])) - TEST(@"+[HMACWithHashClass:] with SHA-1", - (HMACSHA1 = [OFHMAC HMACWithHashClass: [OFSHA1Hash class] - allowsSwappableMemory: true])) - TEST(@"+[HMACWithHashClass:] with RIPEMD-160", - (HMACRMD160 = [OFHMAC HMACWithHashClass: [OFRIPEMD160Hash class] - allowsSwappableMemory: true])) - TEST(@"+[HMACWithHashClass:] with SHA-256", - (HMACSHA256 = [OFHMAC HMACWithHashClass: [OFSHA256Hash class] - allowsSwappableMemory: true])) - TEST(@"+[HMACWithHashClass:] with SHA-384", - (HMACSHA384 = [OFHMAC HMACWithHashClass: [OFSHA384Hash class] - allowsSwappableMemory: true])) - TEST(@"+[HMACWithHashClass:] with SHA-512", - (HMACSHA512 = [OFHMAC HMACWithHashClass: [OFSHA512Hash class] - allowsSwappableMemory: true])) - - EXPECT_EXCEPTION(@"Detection of missing key", - OFInvalidArgumentException, - [HMACMD5 updateWithBuffer: "" length: 0]) - - TEST(@"-[setKey:length:] with MD5", - R([HMACMD5 setKey: key length: keyLength])) - TEST(@"-[setKey:length:] with SHA-1", - R([HMACSHA1 setKey: key length: keyLength])) - TEST(@"-[setKey:length:] with RIPEMD-160", - R([HMACRMD160 setKey: key length: keyLength])) - TEST(@"-[setKey:length:] with SHA-256", - R([HMACSHA256 setKey: key length: keyLength])) - TEST(@"-[setKey:length:] with SHA-384", - R([HMACSHA384 setKey: key length: keyLength])) - TEST(@"-[setKey:length:] with SHA-512", - R([HMACSHA512 setKey: key length: keyLength])) - - while (!file.atEndOfStream) { +@implementation OFHMACTests +- (void)setUp +{ + OFIRI *IRI; + + [super setUp]; + + IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; + _stream = [[OFIRIHandler openItemAtIRI: IRI mode: @"r"] retain]; +} + +- (void)tearDown +{ + [_stream close]; + + [super tearDown]; +} + +- (void)dealloc +{ + [_stream release]; + + [super dealloc]; +} + +- (void)testWithHashClass: (Class)hashClass + expectedDigest: (const unsigned char *)expectedDigest +{ + OFHMAC *HMAC = [OFHMAC HMACWithHashClass: hashClass + allowsSwappableMemory: true]; + + OTAssertNotNil(HMAC); + + OTAssertThrowsSpecific([HMAC updateWithBuffer: "" length: 0], + OFInvalidArgumentException); + + [HMAC setKey: key length: keyLength]; + + while (!_stream.atEndOfStream) { char buffer[64]; - size_t length = [file readIntoBuffer: buffer length: 64]; - [HMACMD5 updateWithBuffer: buffer length: length]; - [HMACSHA1 updateWithBuffer: buffer length: length]; - [HMACRMD160 updateWithBuffer: buffer length: length]; - [HMACSHA256 updateWithBuffer: buffer length: length]; - [HMACSHA384 updateWithBuffer: buffer length: length]; - [HMACSHA512 updateWithBuffer: buffer length: length]; - } - [file close]; - - TEST(@"-[calculate] with MD5", R([HMACMD5 calculate])) - TEST(@"-[calculate] with SHA-1", R([HMACSHA1 calculate])) - TEST(@"-[calculate] with RIPEMD-160", R([HMACRMD160 calculate])) - TEST(@"-[calculate] with SHA-256", R([HMACSHA256 calculate])) - TEST(@"-[calculate] with SHA-384", R([HMACSHA384 calculate])) - TEST(@"-[calculate] with SHA-512", R([HMACSHA512 calculate])) - - TEST(@"-[digest] with MD5", - memcmp(HMACMD5.digest, MD5Digest, HMACMD5.digestSize) == 0) - TEST(@"-[digest] with SHA-1", - memcmp(HMACSHA1.digest, SHA1Digest, HMACSHA1.digestSize) == 0) - TEST(@"-[digest] with RIPEMD-160", - memcmp(HMACRMD160.digest, RIPEMD160Digest, - HMACRMD160.digestSize) == 0) - TEST(@"-[digest] with SHA-256", - memcmp(HMACSHA256.digest, SHA256Digest, HMACSHA256.digestSize) == 0) - TEST(@"-[digest] with SHA-384", - memcmp(HMACSHA384.digest, SHA384Digest, HMACSHA384.digestSize) == 0) - TEST(@"-[digest] with SHA-512", - memcmp(HMACSHA512.digest, SHA512Digest, HMACSHA512.digestSize) == 0) - - objc_autoreleasePoolPop(pool); + size_t length = [_stream readIntoBuffer: buffer length: 64]; + [HMAC updateWithBuffer: buffer length: length]; + } + + [HMAC calculate]; + + OTAssertEqual(memcmp(HMAC.digest, expectedDigest, HMAC.digestSize), 0); +} + +- (void)testHMACWithMD5 +{ + [self testWithHashClass: [OFMD5Hash class] expectedDigest: MD5Digest]; +} + +- (void)testHMACWithRIPEMD160 +{ + [self testWithHashClass: [OFRIPEMD160Hash class] + expectedDigest: RIPEMD160Digest]; +} + +- (void)testHMACWithSHA1 +{ + [self testWithHashClass: [OFSHA1Hash class] expectedDigest: SHA1Digest]; +} + +- (void)testHMACWithSHA256 +{ + [self testWithHashClass: [OFSHA256Hash class] + expectedDigest: SHA256Digest]; +} + +- (void)testHMACWithSHA384 +{ + [self testWithHashClass: [OFSHA384Hash class] + expectedDigest: SHA384Digest]; +} + +- (void)testHMACWithSHA512 +{ + [self testWithHashClass: [OFSHA512Hash class] + expectedDigest: SHA512Digest]; } @end Index: tests/OFHTTPClientTests.m ================================================================== --- tests/OFHTTPClientTests.m +++ tests/OFHTTPClientTests.m @@ -16,72 +16,37 @@ #include "config.h" #include #include -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFHTTPClient"; -static OFCondition *condition; -static OFHTTPResponse *response = nil; - -@interface TestsAppDelegate (HTTPClientTests) +@interface OFHTTPClientTests: OTTestCase +{ + OFHTTPResponse *_response; +} @end @interface HTTPClientTestsServer: OFThread { -@public + OFCondition *_condition; uint16_t _port; } -@end - -@implementation HTTPClientTestsServer -- (id)main -{ - OFTCPSocket *listener, *client; - OFSocketAddress address; - char buffer[5]; - - [condition lock]; - - listener = [OFTCPSocket socket]; - address = [listener bindToHost: @"127.0.0.1" port: 0]; - _port = OFSocketAddressIPPort(&address); - [listener listen]; - - [condition signal]; - [condition unlock]; - - client = [listener accept]; - - OFEnsure([[client readLine] isEqual: @"GET /foo HTTP/1.1"]); - OFEnsure([[client readLine] hasPrefix: @"User-Agent:"]); - OFEnsure([[client readLine] isEqual: @"Content-Length: 5"]); - OFEnsure([[client readLine] isEqual: - @"Content-Type: application/x-www-form-urlencoded; charset=UTF-8"]); - - if (![[client readLine] isEqual: - [OFString stringWithFormat: @"Host: 127.0.0.1:%" @PRIu16, _port]]) - OFEnsure(0); - - OFEnsure([[client readLine] isEqual: @""]); - - [client readIntoBuffer: buffer exactLength: 5]; - OFEnsure(memcmp(buffer, "Hello", 5) == 0); - - [client writeString: @"HTTP/1.0 200 OK\r\n" - @"cONTeNT-lENgTH: 7\r\n" - @"\r\n" - @"foo\n" - @"bar"]; - [client close]; - - return nil; -} -@end - -@implementation TestsAppDelegate (OFHTTPClientTests) + +@property (readonly, nonatomic) OFCondition *condition; +@property (readonly) uint16_t port; +@end + +@implementation OFHTTPClientTests +- (void)dealloc +{ + [_response release]; + + [super dealloc]; +} + - (void)client: (OFHTTPClient *)client wantsRequestBody: (OFStream *)body request: (OFHTTPRequest *)request { [body writeString: @"Hello"]; @@ -90,61 +55,146 @@ - (void)client: (OFHTTPClient *)client didPerformRequest: (OFHTTPRequest *)request response: (OFHTTPResponse *)response_ exception: (id)exception { - OFEnsure(exception == nil); + OTAssertNil(exception); - response = [response_ retain]; + [_response release]; + _response = [response_ retain]; [[OFRunLoop mainRunLoop] stop]; } -- (void)HTTPClientTests +- (void)testClient { - void *pool = objc_autoreleasePoolPush(); HTTPClientTestsServer *server; OFIRI *IRI; - OFHTTPClient *client; OFHTTPRequest *request; + OFHTTPClient *client; OFData *data; - condition = [OFCondition condition]; - [condition lock]; - server = [[[HTTPClientTestsServer alloc] init] autorelease]; server.supportsSockets = true; + + [server.condition lock]; + [server start]; - [condition wait]; - [condition unlock]; + [server.condition wait]; + [server.condition unlock]; IRI = [OFIRI IRIWithString: [OFString stringWithFormat: @"http://127.0.0.1:%" @PRIu16 "/foo", - server->_port]]; - - TEST(@"-[asyncPerformRequest:]", - (client = [OFHTTPClient client]) && (client.delegate = self) && - (request = [OFHTTPRequest requestWithIRI: IRI]) && - (request.headers = - [OFDictionary dictionaryWithObject: @"5" - forKey: @"Content-Length"]) && - R([client asyncPerformRequest: request])) + server.port]]; + + request = [OFHTTPRequest requestWithIRI: IRI]; + request.headers = [OFDictionary + dictionaryWithObject: @"5" + forKey: @"Content-Length"]; + + client = [OFHTTPClient client]; + client.delegate = self; + [client asyncPerformRequest: request]; [[OFRunLoop mainRunLoop] runUntilDate: [OFDate dateWithTimeIntervalSinceNow: 2]]; - [response autorelease]; - - TEST(@"Asynchronous handling of requests", response != nil) - - TEST(@"Normalization of server header keys", - [response.headers objectForKey: @"Content-Length"] != nil) - - TEST(@"Correct parsing of data", - (data = [response readDataUntilEndOfStream]) && - data.count == 7 && memcmp(data.items, "foo\nbar", 7) == 0) - - [server join]; - - objc_autoreleasePoolPop(pool); + + OTAssertNotNil(_response); + OTAssertNotNil([_response.headers objectForKey: @"Content-Length"]); + + data = [_response readDataUntilEndOfStream]; + OTAssertEqual(data.count, 7); + OTAssertEqual(data.itemSize, 1); + OTAssertEqual(memcmp(data.items, "foo\nbar", 7), 0); + + OTAssertNil([server join]); +} +@end + +@implementation HTTPClientTestsServer +@synthesize condition = _condition, port = _port; + +- (instancetype)init +{ + self = [super init]; + + @try { + _condition = [[OFCondition alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_condition release]; + + [super dealloc]; +} + +- (id)main +{ + OFTCPSocket *listener, *client; + OFSocketAddress address; + bool sawHost = false, sawContentLength = false, sawContentType = false; + bool sawUserAgent = false; + char buffer[5]; + + [_condition lock]; + + listener = [OFTCPSocket socket]; + address = [listener bindToHost: @"127.0.0.1" port: 0]; + _port = OFSocketAddressIPPort(&address); + [listener listen]; + + [_condition signal]; + [_condition unlock]; + client = [listener accept]; + + if (![[client readLine] isEqual: @"GET /foo HTTP/1.1"]) + return @"Wrong request"; + + for (size_t i = 0; i < 4; i++) { + OFString *line = [client readLine]; + + if ([line isEqual: [OFString stringWithFormat: + @"Host: 127.0.0.1:%" @PRIu16, _port]]) + sawHost = true; + else if ([line isEqual: @"Content-Length: 5"]) + sawContentLength = true; + if ([line isEqual: @"Content-Type: application/" + @"x-www-form-urlencoded; charset=UTF-8"]) + sawContentType = true; + else if ([line hasPrefix: @"User-Agent:"]) + sawUserAgent = true; + } + + if (!sawHost) + return @"Missing host"; + if (!sawContentLength) + return @"Missing content length"; + if (!sawContentType) + return @"Missing content type"; + if (!sawUserAgent) + return @"Missing user agent"; + + if (![[client readLine] isEqual: @""]) + return @"Missing empty line"; + + [client readIntoBuffer: buffer exactLength: 5]; + if (memcmp(buffer, "Hello", 5) != 0) + return @"Missing body"; + + [client writeString: @"HTTP/1.0 200 OK\r\n" + @"cONTeNT-lENgTH: 7\r\n" + @"\r\n" + @"foo\n" + @"bar"]; + [client close]; + + return nil; } @end Index: tests/OFHTTPCookieManagerTests.m ================================================================== --- tests/OFHTTPCookieManagerTests.m +++ tests/OFHTTPCookieManagerTests.m @@ -13,18 +13,19 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFHTTPCookieManager"; - -@implementation TestsAppDelegate (OFHTTPCookieManagerTests) -- (void)HTTPCookieManagerTests -{ - void *pool = objc_autoreleasePoolPush(); +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFHTTPCookieManagerTests: OTTestCase +@end + +@implementation OFHTTPCookieManagerTests +- (void)testCookieManager +{ OFHTTPCookieManager *manager = [OFHTTPCookieManager manager]; OFIRI *IRI1, *IRI2, *IRI3, *IRI4; OFHTTPCookie *cookie1, *cookie2, *cookie3, *cookie4, *cookie5; IRI1 = [OFIRI IRIWithString: @"http://nil.im/foo"]; @@ -33,71 +34,53 @@ IRI4 = [OFIRI IRIWithString: @"http://webkeks.org/foo/bar"]; cookie1 = [OFHTTPCookie cookieWithName: @"test" value: @"1" domain: @"nil.im"]; - TEST(@"-[addCookie:forIRI:] #1", - R([manager addCookie: cookie1 forIRI: IRI1])) - - TEST(@"-[cookiesForIRI:] #1", - [[manager cookiesForIRI: IRI1] isEqual: - [OFArray arrayWithObject: cookie1]]) + [manager addCookie: cookie1 forIRI: IRI1]; + OTAssertEqualObjects([manager cookiesForIRI: IRI1], + [OFArray arrayWithObject: cookie1]); cookie2 = [OFHTTPCookie cookieWithName: @"test" value: @"2" domain: @"webkeks.org"]; - TEST(@"-[addCookie:forIRI:] #2", - R([manager addCookie: cookie2 forIRI: IRI1])) - - TEST(@"-[cookiesForIRI:] #2", - [[manager cookiesForIRI: IRI1] isEqual: - [OFArray arrayWithObject: cookie1]] && - [[manager cookiesForIRI: IRI4] isEqual: [OFArray array]]) + [manager addCookie: cookie2 forIRI: IRI1]; + OTAssertEqualObjects([manager cookiesForIRI: IRI1], + [OFArray arrayWithObject: cookie1]); + OTAssertEqualObjects([manager cookiesForIRI: IRI4], [OFArray array]); cookie3 = [OFHTTPCookie cookieWithName: @"test" value: @"3" domain: @"nil.im"]; cookie3.secure = true; - TEST(@"-[addCookie:forIRI:] #3", - R([manager addCookie: cookie3 forIRI: IRI2])) - - TEST(@"-[cookiesForIRI:] #3", - [[manager cookiesForIRI: IRI2] isEqual: - [OFArray arrayWithObject: cookie3]] && - [[manager cookiesForIRI: IRI1] isEqual: [OFArray array]]) + [manager addCookie: cookie3 forIRI: IRI2]; + OTAssertEqualObjects([manager cookiesForIRI: IRI2], + [OFArray arrayWithObject: cookie3]); + OTAssertEqualObjects([manager cookiesForIRI: IRI1], [OFArray array]); cookie3.expires = [OFDate dateWithTimeIntervalSinceNow: -1]; cookie4 = [OFHTTPCookie cookieWithName: @"test" value: @"4" domain: @"nil.im"]; cookie4.domain = @".nil.im"; - TEST(@"-[addCookie:forIRI:] #4", - R([manager addCookie: cookie4 forIRI: IRI2])) - - TEST(@"-[cookiesForIRI:] #4", - [[manager cookiesForIRI: IRI2] isEqual: - [OFArray arrayWithObject: cookie4]] && - [[manager cookiesForIRI: IRI3] isEqual: - [OFArray arrayWithObject: cookie4]]) + [manager addCookie: cookie4 forIRI: IRI2]; + OTAssertEqualObjects([manager cookiesForIRI: IRI2], + [OFArray arrayWithObject: cookie4]); + OTAssertEqualObjects([manager cookiesForIRI: IRI3], + [OFArray arrayWithObject: cookie4]); cookie5 = [OFHTTPCookie cookieWithName: @"bar" value: @"5" domain: @"test.nil.im"]; - TEST(@"-[addCookie:forIRI:] #5", - R([manager addCookie: cookie5 forIRI: IRI1])) - - TEST(@"-[cookiesForIRI:] #5", - [[manager cookiesForIRI: IRI1] isEqual: - [OFArray arrayWithObject: cookie4]] && - [[manager cookiesForIRI: IRI3] isEqual: - [OFArray arrayWithObjects: cookie4, cookie5, nil]]) - - TEST(@"-[purgeExpiredCookies]", - [manager.cookies isEqual: - [OFArray arrayWithObjects: cookie3, cookie4, cookie5, nil]] && - R([manager purgeExpiredCookies]) && - [manager.cookies isEqual: - [OFArray arrayWithObjects: cookie4, cookie5, nil]]) - - objc_autoreleasePoolPop(pool); + [manager addCookie: cookie5 forIRI: IRI1]; + OTAssertEqualObjects([manager cookiesForIRI: IRI1], + [OFArray arrayWithObject: cookie4]); + OTAssertEqualObjects([manager cookiesForIRI: IRI3], + ([OFArray arrayWithObjects: cookie4, cookie5, nil])); + + OTAssertEqualObjects(manager.cookies, + ([OFArray arrayWithObjects: cookie3, cookie4, cookie5, nil])); + [manager purgeExpiredCookies]; + OTAssertEqualObjects(manager.cookies, + ([OFArray arrayWithObjects: cookie4, cookie5, nil])); } @end Index: tests/OFHTTPCookieTests.m ================================================================== --- tests/OFHTTPCookieTests.m +++ tests/OFHTTPCookieTests.m @@ -13,39 +13,39 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFHTTPCookie"; - -@implementation TestsAppDelegate (OFHTTPCookieTests) -- (void)HTTPCookieTests -{ - void *pool = objc_autoreleasePoolPush(); +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFHTTPCookieTests: OTTestCase +@end + +@implementation OFHTTPCookieTests +- (void)testCookiesWithResponseHeaderFieldsForIRI +{ OFIRI *IRI = [OFIRI IRIWithString: @"http://nil.im"]; - OFHTTPCookie *cookie1, *cookie2; - OFArray OF_GENERIC(OFHTTPCookie *) *cookies; - - cookie1 = [OFHTTPCookie cookieWithName: @"foo" - value: @"bar" - domain: @"nil.im"]; - TEST(@"+[cookiesWithResponseHeaderFields:forIRI:] #1", - [[OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary - dictionaryWithObject: @"foo=bar" - forKey: @"Set-Cookie"] forIRI: IRI] - isEqual: [OFArray arrayWithObject: cookie1]]) - - cookie2 = [OFHTTPCookie cookieWithName: @"qux" - value: @"cookie" - domain: @"nil.im"]; - TEST(@"+[cookiesWithResponseHeaderFields:forIRI:] #2", - [[OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary - dictionaryWithObject: @"foo=bar,qux=cookie" - forKey: @"Set-Cookie"] forIRI: IRI] - isEqual: [OFArray arrayWithObjects: cookie1, cookie2, nil]]) + OFHTTPCookie *cookie1 = [OFHTTPCookie cookieWithName: @"foo" + value: @"bar" + domain: @"nil.im"]; + OFHTTPCookie *cookie2 = [OFHTTPCookie cookieWithName: @"qux" + value: @"cookie" + domain: @"nil.im"]; + OFDictionary *headers; + + headers = [OFDictionary dictionaryWithObject: @"foo=bar" + forKey: @"Set-Cookie"]; + OTAssertEqualObjects( + [OFHTTPCookie cookiesWithResponseHeaderFields: headers forIRI: IRI], + [OFArray arrayWithObject: cookie1]); + + headers = [OFDictionary dictionaryWithObject: @"foo=bar,qux=cookie" + forKey: @"Set-Cookie"]; + OTAssertEqualObjects( + [OFHTTPCookie cookiesWithResponseHeaderFields: headers forIRI: IRI], + ([OFArray arrayWithObjects: cookie1, cookie2, nil])); cookie1.expires = [OFDate dateWithTimeIntervalSince1970: 1234567890]; cookie2.expires = [OFDate dateWithTimeIntervalSince1970: 1234567890]; cookie1.path = @"/x"; cookie2.domain = @"webkeks.org"; @@ -52,22 +52,39 @@ cookie2.path = @"/objfw"; cookie2.secure = true; cookie2.HTTPOnly = true; [cookie2.extensions addObject: @"foo"]; [cookie2.extensions addObject: @"bar"]; - TEST(@"+[cookiesWithResponseHeaderFields:forIRI:] #3", - [(cookies = [OFHTTPCookie cookiesWithResponseHeaderFields: - [OFDictionary dictionaryWithObject: - @"foo=bar; Expires=Fri, 13 Feb 2009 23:31:30 GMT; Path=/x," - @"qux=cookie; Expires=Fri, 13 Feb 2009 23:31:30 GMT; " - @"Domain=webkeks.org; Path=/objfw; Secure; HTTPOnly; foo; bar" - forKey: @"Set-Cookie"] forIRI: IRI]) isEqual: - [OFArray arrayWithObjects: cookie1, cookie2, nil]]) - - TEST(@"+[requestHeaderFieldsWithCookies:]", - [[OFHTTPCookie requestHeaderFieldsWithCookies: cookies] isEqual: - [OFDictionary dictionaryWithObject: @"foo=bar; qux=cookie" - forKey: @"Cookie"]]) - - objc_autoreleasePoolPop(pool); + + headers = [OFDictionary + dictionaryWithObject: @"foo=bar; " + @"Expires=Fri, 13 Feb 2009 23:31:30 GMT; " + @"Path=/x," + @"qux=cookie; " + @"Expires=Fri, 13 Feb 2009 23:31:30 GMT; " + @"Domain=webkeks.org; " + @"Path=/objfw; " + @"Secure; " + @"HTTPOnly; " + @"foo; " + @"bar" + forKey: @"Set-Cookie"]; + OTAssertEqualObjects( + [OFHTTPCookie cookiesWithResponseHeaderFields: headers forIRI: IRI], + ([OFArray arrayWithObjects: cookie1, cookie2, nil])); +} + +- (void)testRequestHeaderFieldsWithCookies +{ + OFHTTPCookie *cookie1 = [OFHTTPCookie cookieWithName: @"foo" + value: @"bar" + domain: @"nil.im"]; + OFHTTPCookie *cookie2 = [OFHTTPCookie cookieWithName: @"qux" + value: @"cookie" + domain: @"nil.im"]; + + OTAssertEqualObjects([OFHTTPCookie requestHeaderFieldsWithCookies: + ([OFArray arrayWithObjects: cookie1, cookie2, nil])], + [OFDictionary dictionaryWithObject: @"foo=bar; qux=cookie" + forKey: @"Cookie"]); } @end Index: tests/OFINIFileTests.m ================================================================== --- tests/OFINIFileTests.m +++ tests/OFINIFileTests.m @@ -13,19 +13,101 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *module; - -@implementation TestsAppDelegate (OFINIFileTests) -- (void)INIFileTests -{ - void *pool = objc_autoreleasePoolPush(); - OFString *output = @"[tests]\r\n" +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFINIFileTests: OTTestCase +{ + OFINIFile *_file; +} +@end + +@implementation OFINIFileTests +- (void)setUp +{ + OFIRI *IRI; + + [super setUp]; + + IRI = [OFIRI IRIWithString: @"embedded:testfile.ini"]; + _file = [[OFINIFile alloc] initWithIRI: IRI + encoding: OFStringEncodingISO8859_1]; +} + +- (void)dealloc +{ + [_file release]; + + [super dealloc]; +} + +- (void)testCategoryForName +{ + OTAssertNotNil([_file categoryForName: @"tests"]); + OTAssertNotNil([_file categoryForName: @"foobar"]); + OTAssertNotNil([_file categoryForName: @"types"]); +} + +- (void)testStringValueForKey +{ + OTAssertEqualObjects( + [[_file categoryForName: @"tests"] stringValueForKey: @"foo"], + @"bar"); + + OTAssertEqualObjects([[_file categoryForName: @"foobar"] + stringValueForKey: @"quxquxqux"], + @"hello\"wörld"); +} + +- (void)testLongLongValueForKeyDefaultValue +{ + OTAssertEqual([[_file categoryForName: @"types"] + longLongValueForKey: @"integer" + defaultValue: 2], + 0x20); +} + +- (void)testBoolValueForKeyDefaultValue +{ + OTAssertTrue([[_file categoryForName: @"types"] + boolValueForKey: @"bool" + defaultValue: false]); +} + +- (void)testFloatValueForKeyDefaultValue +{ + OTAssertEqual([[_file categoryForName: @"types"] + floatValueForKey: @"float" + defaultValue: 1], + 0.5f); +} + +- (void)testDoubleValueForKeyDefaultValue +{ + OTAssertEqual([[_file categoryForName: @"types"] + doubleValueForKey: @"double" + defaultValue: 3], + 0.25); +} + +- (void)testArrayValueForKey +{ + OFINICategory *types = [_file categoryForName: @"types"]; + OFArray *array = [OFArray arrayWithObjects: @"1", @"2", nil]; + + OTAssertEqualObjects([types arrayValueForKey: @"array1"], array); + OTAssertEqualObjects([types arrayValueForKey: @"array2"], array); + OTAssertEqualObjects([types arrayValueForKey: @"array3"], + [OFArray array]); +} + +- (void)testWriteToIRIEncoding +{ + OFString *expectedOutput = @"[tests]\r\n" @"foo=baz\r\n" @"foobar=baz\r\n" @";comment\r\n" @"new=new\r\n" @"\r\n" @@ -41,98 +123,49 @@ @"bool=false\r\n" @"float=0.25\r\n" @"array1=foo\r\n" @"array1=bar\r\n" @"double=0.75\r\n"; - OFIRI *IRI; - OFINIFile *file; - OFINICategory *tests, *foobar, *types; - OFArray *array; + OFINICategory *tests = [_file categoryForName: @"tests"]; + OFINICategory *foobar = [_file categoryForName: @"foobar"]; + OFINICategory *types = [_file categoryForName: @"types"]; + OFArray *array = [OFArray arrayWithObjects: @"foo", @"bar", nil]; #if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_DS) OFIRI *writeIRI; #endif - module = @"OFINIFile"; - - IRI = [OFIRI IRIWithString: @"embedded:testfile.ini"]; - TEST(@"+[fileWithIRI:encoding:]", - (file = [OFINIFile fileWithIRI: IRI - encoding: OFStringEncodingCodepage437])) - - tests = [file categoryForName: @"tests"]; - foobar = [file categoryForName: @"foobar"]; - types = [file categoryForName: @"types"]; - TEST(@"-[categoryForName:]", - tests != nil && foobar != nil && types != nil) - - module = @"OFINICategory"; - - TEST(@"-[stringValueForKey:]", - [[tests stringValueForKey: @"foo"] isEqual: @"bar"] && - [[foobar stringValueForKey: @"quxquxqux"] isEqual: @"hello\"wörld"]) - - TEST(@"-[setStringValue:forKey:]", - R([tests setStringValue: @"baz" forKey: @"foo"]) && - R([tests setStringValue: @"new" forKey: @"new"]) && - R([foobar setStringValue: @"a\fb" forKey: @"qux3"])) - - TEST(@"-[longLongValueForKey:defaultValue:]", - [types longLongValueForKey: @"integer" defaultValue: 2] == 0x20) - - TEST(@"-[setLongLongValue:forKey:]", - R([types setLongLongValue: 0x10 forKey: @"integer"])) - - TEST(@"-[boolValueForKey:defaultValue:]", - [types boolValueForKey: @"bool" defaultValue: false] == true) - - TEST(@"-[setBoolValue:forKey:]", - R([types setBoolValue: false forKey: @"bool"])) - - TEST(@"-[floatValueForKey:defaultValue:]", - [types floatValueForKey: @"float" defaultValue: 1] == 0.5f) - - TEST(@"-[setFloatValue:forKey:]", - R([types setFloatValue: 0.25f forKey: @"float"])) - - TEST(@"-[doubleValueForKey:defaultValue:]", - [types doubleValueForKey: @"double" defaultValue: 3] == 0.25) - - TEST(@"-[setDoubleValue:forKey:]", - R([types setDoubleValue: 0.75 forKey: @"double"])) - - array = [OFArray arrayWithObjects: @"1", @"2", nil]; - TEST(@"-[arrayValueForKey:]", - [[types arrayValueForKey: @"array1"] isEqual: array] && - [[types arrayValueForKey: @"array2"] isEqual: array] && - [[types arrayValueForKey: @"array3"] isEqual: [OFArray array]]) - - array = [OFArray arrayWithObjects: @"foo", @"bar", nil]; - TEST(@"-[setArrayValue:forKey:]", - R([types setArrayValue: array forKey: @"array1"])) - - TEST(@"-[removeValueForKey:]", - R([foobar removeValueForKey: @"quxqux "]) && - R([types removeValueForKey: @"array2"])) - - module = @"OFINIFile"; + [tests setStringValue: @"baz" forKey: @"foo"]; + [tests setStringValue: @"new" forKey: @"new"]; + [foobar setStringValue: @"a\fb" forKey: @"qux3"]; + [types setLongLongValue: 0x10 forKey: @"integer"]; + [types setBoolValue: false forKey: @"bool"]; + [types setFloatValue: 0.25f forKey: @"float"]; + [types setDoubleValue: 0.75 forKey: @"double"]; + [types setArrayValue: array forKey: @"array1"]; + + [foobar removeValueForKey: @"quxqux "]; + [types removeValueForKey: @"array2"]; /* FIXME: Find a way to write files on Nintendo DS */ #if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_DS) writeIRI = [OFSystemInfo temporaryDirectoryIRI]; if (writeIRI == nil) writeIRI = [[OFFileManager defaultManager] currentDirectoryIRI]; writeIRI = [writeIRI IRIByAppendingPathComponent: @"objfw-tests.ini" isDirectory: false]; - TEST(@"-[writeToFile:encoding:]", - R([file writeToIRI: writeIRI - encoding: OFStringEncodingCodepage437]) && - [[OFString stringWithContentsOfIRI: writeIRI - encoding: OFStringEncodingCodepage437] - isEqual: output]) - [[OFFileManager defaultManager] removeItemAtIRI: writeIRI]; + + [_file writeToIRI: writeIRI + encoding: OFStringEncodingISO8859_1]; + + @try { + OTAssertEqualObjects([OFString + stringWithContentsOfIRI: writeIRI + encoding: OFStringEncodingISO8859_1], + expectedOutput); + } @finally { + [[OFFileManager defaultManager] removeItemAtIRI: writeIRI]; + } #else - (void)output; + (void)expectedOutput; #endif - - objc_autoreleasePoolPop(pool); } @end Index: tests/OFIPXSocketTests.m ================================================================== --- tests/OFIPXSocketTests.m +++ tests/OFIPXSocketTests.m @@ -14,56 +14,43 @@ */ #include "config.h" #include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFIPXSocket"; - -@implementation TestsAppDelegate (OFIPXSocketTests) -- (void)IPXSocketTests -{ - const unsigned char zeroNode[IPX_NODE_LEN] = { 0 }; - void *pool = objc_autoreleasePoolPush(); - OFIPXSocket *sock; +#include + +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFIPXSocketTests: OTTestCase +@end + +@implementation OFIPXSocketTests +- (void)testIPXSocket +{ + OFIPXSocket *sock = [OFIPXSocket socket]; + const unsigned char zeroNode[IPX_NODE_LEN] = { 0 }; OFSocketAddress address1, address2; OFDictionary *networkInterfaces; char buffer[5]; unsigned char node1[IPX_NODE_LEN], node2[IPX_NODE_LEN]; unsigned char node[IPX_NODE_LEN]; - TEST(@"+[socket]", (sock = [OFIPXSocket socket])) - @try { - TEST(@"-[bindToNetwork:node:port:packetType:]", - R(address1 = [sock bindToNetwork: 0 - node: zeroNode - port: 0 - packetType: 0])) + address1 = [sock bindToNetwork: 0 + node: zeroNode + port: 0 + packetType: 0]; } @catch (OFBindSocketFailedException *e) { switch (e.errNo) { case EAFNOSUPPORT: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFIPXSocket] -[bindToNetwork:node:port:" - @"packetType:]: IPX unsupported, skipping tests"]; - break; + OTSkip(@"IPX unsupported"); case EADDRNOTAVAIL: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFIPXSocket] -[bindToNetwork:node:port:" - @"packetType:]: IPX not configured, skipping " - @"tests"]; - break; + OTSkip(@"IPX not configured"); default: @throw e; } - - objc_autoreleasePoolPop(pool); - return; } /* * Find any network interface with IPX and send to it. Any should be * fine since we bound to 0.0. @@ -84,29 +71,21 @@ OFSocketAddressSetIPXNode(&address1, node); } OFSocketAddressGetIPXNode(&address1, node); if (OFSocketAddressIPXNetwork(&address1) == 0 && - memcmp(node, zeroNode, 6) == 0) { - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"[OFIPXSocket] -[sendBuffer:length:receiver:]: " - @"Could not determine own address, skipping tests"]; - objc_autoreleasePoolPop(pool); - return; - } - - TEST(@"-[sendBuffer:length:receiver:]", - R([sock sendBuffer: "Hello" length: 5 receiver: &address1])) - - TEST(@"-[receiveIntoBuffer:length:sender:]", - [sock receiveIntoBuffer: buffer length: 5 sender: &address2] == 5 && - memcmp(buffer, "Hello", 5) == 0 && - R(OFSocketAddressGetIPXNode(&address1, node1)) && - R(OFSocketAddressGetIPXNode(&address2, node2)) && - memcmp(node1, node2, IPX_NODE_LEN) == 0 && - OFSocketAddressIPXPort(&address1) == - OFSocketAddressIPXPort(&address2)) - - objc_autoreleasePoolPop(pool); + memcmp(node, zeroNode, 6) == 0) + OTSkip(@"Could not determine own IPX address"); + + [sock sendBuffer: "Hello" length: 5 receiver: &address1]; + + OTAssertEqual([sock receiveIntoBuffer: buffer + length: 5 + sender: &address2], 5); + OTAssertEqual(memcmp(buffer, "Hello", 5), 0); + OFSocketAddressGetIPXNode(&address1, node1); + OFSocketAddressGetIPXNode(&address2, node2); + OTAssertEqual(memcmp(node1, node2, IPX_NODE_LEN), 0); + OTAssertEqual(OFSocketAddressIPXPort(&address1), + OFSocketAddressIPXPort(&address2)); } @end Index: tests/OFIRITests.m ================================================================== --- tests/OFIRITests.m +++ tests/OFIRITests.m @@ -13,359 +13,527 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFIRI"; -static OFString *IRIString = @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/" +@interface OFIRITests: OTTestCase +{ + OFIRI *_IRI[11]; + OFMutableIRI *_mutableIRI; +} +@end + +static OFString *IRI0String = @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/" @"pa%3Fth?que%23ry=1&f%26oo=b%3dar#frag%23ment"; -@implementation TestsAppDelegate (OFIRITests) -- (void)IRITests -{ - void *pool = objc_autoreleasePoolPush(); - OFIRI *IRI1, *IRI2, *IRI3, *IRI4, *IRI5, *IRI6, *IRI7, *IRI8, *IRI9; - OFIRI *IRI10, *IRI11; - OFMutableIRI *mutableIRI; - - TEST(@"+[IRIWithString:]", - R(IRI1 = [OFIRI IRIWithString: IRIString]) && - R(IRI2 = [OFIRI IRIWithString: @"http://foo:80"]) && - R(IRI3 = [OFIRI IRIWithString: @"http://bar/"]) && - R(IRI4 = [OFIRI IRIWithString: @"file:///etc/passwd"]) && - R(IRI5 = [OFIRI IRIWithString: @"http://foo/bar/qux/foo%2fbar"]) && - R(IRI6 = [OFIRI IRIWithString: @"https://[12:34::56:abcd]/"]) && - R(IRI7 = [OFIRI IRIWithString: @"https://[12:34::56:abcd]:234/"]) && - R(IRI8 = [OFIRI IRIWithString: @"urn:qux:foo"]) && - R(IRI9 = [OFIRI IRIWithString: @"file:/foo?query#frag"]) && - R(IRI10 = [OFIRI IRIWithString: @"file:foo@bar/qux?query#frag"]) && - R(IRI11 = [OFIRI IRIWithString: @"http://ä/ö?ü"])) - - EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #1", - OFInvalidFormatException, - [OFIRI IRIWithString: @"ht,tp://foo"]) - - EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #2", - OFInvalidFormatException, - [OFIRI IRIWithString: @"http://f`oo"]) - - EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #3", - OFInvalidFormatException, - [OFIRI IRIWithString: @"http://foo/`"]) - - EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #4", - OFInvalidFormatException, - [OFIRI IRIWithString: @"http://foo/foo?`"]) - - EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #5", - OFInvalidFormatException, - [OFIRI IRIWithString: @"http://foo/foo?foo#`"]) - - EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #6", - OFInvalidFormatException, - [OFIRI IRIWithString: @"https://[g]/"]) - - EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #7", - OFInvalidFormatException, - [OFIRI IRIWithString: @"https://[f]:/"]) - - EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #8", - OFInvalidFormatException, - [OFIRI IRIWithString: @"https://[f]:f/"]) - - EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #9", - OFInvalidFormatException, - [OFIRI IRIWithString: @"foo:"]) - - TEST(@"+[IRIWithString:relativeToIRI:]", - [[[OFIRI IRIWithString: @"/foo" relativeToIRI: IRI1] string] - isEqual: @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/foo"] && - [[[OFIRI IRIWithString: @"foo/bar?q" - relativeToIRI: [OFIRI IRIWithString: @"http://h/qux/quux"]] - string] isEqual: @"http://h/qux/foo/bar?q"] && - [[[OFIRI IRIWithString: @"foo/bar" - relativeToIRI: [OFIRI IRIWithString: @"http://h/qux/?x"]] - string] isEqual: @"http://h/qux/foo/bar"] && - [[[OFIRI IRIWithString: @"http://foo/?q" - relativeToIRI: IRI1] string] isEqual: @"http://foo/?q"] && - [[[OFIRI IRIWithString: @"foo" - relativeToIRI: [OFIRI IRIWithString: @"http://foo/bar"]] - string] isEqual: @"http://foo/foo"] && - [[[OFIRI IRIWithString: @"foo" - relativeToIRI: [OFIRI IRIWithString: @"http://foo"]] - string] isEqual: @"http://foo/foo"]) - - EXPECT_EXCEPTION( - @"+[IRIWithString:relativeToIRI:] fails with invalid characters #1", - OFInvalidFormatException, - [OFIRI IRIWithString: @"`" relativeToIRI: IRI1]) - - EXPECT_EXCEPTION( - @"+[IRIWithString:relativeToIRI:] fails with invalid characters #2", - OFInvalidFormatException, - [OFIRI IRIWithString: @"/`" relativeToIRI: IRI1]) - - EXPECT_EXCEPTION( - @"+[IRIWithString:relativeToIRI:] fails with invalid characters #3", - OFInvalidFormatException, - [OFIRI IRIWithString: @"?`" relativeToIRI: IRI1]) - - EXPECT_EXCEPTION( - @"+[IRIWithString:relativeToIRI:] fails with invalid characters #4", - OFInvalidFormatException, - [OFIRI IRIWithString: @"#`" relativeToIRI: IRI1]) +@implementation OFIRITests +- (void)setUp +{ + [super setUp]; + + _IRI[0] = [[OFIRI alloc] initWithString: IRI0String]; + _IRI[1] = [[OFIRI alloc] initWithString: @"http://foo:80"]; + _IRI[2] = [[OFIRI alloc] initWithString: @"http://bar/"]; + _IRI[3] = [[OFIRI alloc] initWithString: @"file:///etc/passwd"]; + _IRI[4] = [[OFIRI alloc] + initWithString: @"http://foo/bar/qux/foo%2fbar"]; + _IRI[5] = [[OFIRI alloc] initWithString: @"https://[12:34::56:abcd]/"]; + _IRI[6] = [[OFIRI alloc] + initWithString: @"https://[12:34::56:abcd]:234/"]; + _IRI[7] = [[OFIRI alloc] initWithString: @"urn:qux:foo"]; + _IRI[8] = [[OFIRI alloc] initWithString: @"file:/foo?query#frag"]; + _IRI[9] = [[OFIRI alloc] + initWithString: @"file:foo@bar/qux?query#frag"]; + _IRI[10] = [[OFIRI alloc] initWithString: @"http://ä/ö?ü"]; + + _mutableIRI = [[OFMutableIRI alloc] initWithScheme: @"dummy"]; +} + +- (void)dealloc +{ + for (uint_fast8_t i = 0; i < 11; i++) + [_IRI[i] release]; + + [_mutableIRI release]; + + [super dealloc]; +} + +- (void)testIRIWithStringFailsWithInvalidCharacters +{ + OTAssertThrowsSpecific([OFIRI IRIWithString: @"ht,tp://foo"], + OFInvalidFormatException); + + OTAssertThrowsSpecific([OFIRI IRIWithString: @"http://f`oo"], + OFInvalidFormatException); + + OTAssertThrowsSpecific([OFIRI IRIWithString: @"http://foo/`"], + OFInvalidFormatException); + + OTAssertThrowsSpecific([OFIRI IRIWithString: @"http://foo/foo?`"], + OFInvalidFormatException); + + OTAssertThrowsSpecific([OFIRI IRIWithString: @"http://foo/foo?foo#`"], + OFInvalidFormatException); + + OTAssertThrowsSpecific([OFIRI IRIWithString: @"https://[g]/"], + OFInvalidFormatException); + + OTAssertThrowsSpecific([OFIRI IRIWithString: @"https://[f]:/"], + OFInvalidFormatException); + + OTAssertThrowsSpecific([OFIRI IRIWithString: @"https://[f]:f/"], + OFInvalidFormatException); + + OTAssertThrowsSpecific([OFIRI IRIWithString: @"foo:"], + OFInvalidFormatException); +} + +- (void)testIRIWithStringRelativeToIRI +{ + OTAssertEqualObjects([[OFIRI IRIWithString: @"/foo" + relativeToIRI: _IRI[0]] string], + @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/foo"); + + OTAssertEqualObjects( + [[OFIRI IRIWithString: @"foo/bar?q" + relativeToIRI: [OFIRI IRIWithString: @"http://h/qux/quux"]] + string], + @"http://h/qux/foo/bar?q"); + + OTAssertEqualObjects( + [[OFIRI IRIWithString: @"foo/bar" + relativeToIRI: [OFIRI IRIWithString: @"http://h/qux/?x"]] + string], + @"http://h/qux/foo/bar"); + + OTAssertEqualObjects([[OFIRI IRIWithString: @"http://foo/?q" + relativeToIRI: _IRI[0]] string], + @"http://foo/?q"); + + OTAssertEqualObjects( + [[OFIRI IRIWithString: @"foo" + relativeToIRI: [OFIRI IRIWithString: @"http://foo/bar"]] + string], + @"http://foo/foo"); + + OTAssertEqualObjects( + [[OFIRI IRIWithString: @"foo" + relativeToIRI: [OFIRI IRIWithString: @"http://foo"]] + string], + @"http://foo/foo"); +} + +- (void)testIRIWithStringRelativeToIRIFailsWithInvalidCharacters +{ + OTAssertThrowsSpecific( + [OFIRI IRIWithString: @"`" relativeToIRI: _IRI[0]], + OFInvalidFormatException); + + OTAssertThrowsSpecific( + [OFIRI IRIWithString: @"/`" relativeToIRI: _IRI[0]], + OFInvalidFormatException); + + OTAssertThrowsSpecific( + [OFIRI IRIWithString: @"?`" relativeToIRI: _IRI[0]], + OFInvalidFormatException); + + OTAssertThrowsSpecific( + [OFIRI IRIWithString: @"#`" relativeToIRI: _IRI[0]], + OFInvalidFormatException); +} #ifdef OF_HAVE_FILES - TEST(@"+[fileIRIWithPath:]", - [[[OFIRI fileIRIWithPath: @"testfile.txt"] fileSystemRepresentation] - isEqual: [[OFFileManager defaultManager].currentDirectoryPath - stringByAppendingPathComponent: @"testfile.txt"]]) +- (void)testFileIRIWithPath +{ + OTAssertEqualObjects( + [[OFIRI fileIRIWithPath: @"testfile.txt"] fileSystemRepresentation], + [[OFFileManager defaultManager].currentDirectoryPath + stringByAppendingPathComponent: @"testfile.txt"]); +} # if defined(OF_WINDOWS) || defined(OF_MSDOS) - OFIRI *tmp; - TEST(@"+[fileIRIWithPath:] for c:\\", - (tmp = [OFIRI fileIRIWithPath: @"c:\\"]) && - [tmp.string isEqual: @"file:/c:/"] && - [tmp.fileSystemRepresentation isEqual: @"c:\\"]) +- (void)testFileIRWithPathC +{ + OFIRI *IRI = [OFIRI fileIRIWithPath: @"c:\\"]; + OTAssertEqualObjects(IRI.string, @"file:/c:/"); + OTAssertEqualObjects(IRI.fileSystemRepresentation, @"c:\\"); +} # endif # ifdef OF_WINDOWS - TEST(@"+[fileIRIWithPath:] with UNC", - (tmp = [OFIRI fileIRIWithPath: @"\\\\foo\\bar" - isDirectory: false]) && - [tmp.host isEqual: @"foo"] && [tmp.path isEqual: @"/bar"] && - [tmp.string isEqual: @"file://foo/bar"] && - [tmp.fileSystemRepresentation isEqual: @"\\\\foo\\bar"] && - (tmp = [OFIRI fileIRIWithPath: @"\\\\test" isDirectory: true]) && - [tmp.host isEqual: @"test"] && [tmp.path isEqual: @"/"] && - [tmp.string isEqual: @"file://test/"] && - [tmp.fileSystemRepresentation isEqual: @"\\\\test"]) +- (void)testFileIRIWithPathUNC +{ + OFIRI *IRI; + + IRI = [OFIRI fileIRIWithPath: @"\\\\foo\\bar" isDirectory: false]; + OTAssertEqualObjects(IRI.host, @"foo"); + OTAssertEqualObjects(IRI.path, @"/bar"); + OTAssertEqualObjects(IRI.string, @"file://foo/bar"); + OTAssertEqualObjects(IRI.fileSystemRepresentation, @"\\\\foo\\bar"); + + IRI = [OFIRI fileIRIWithPath: @"\\\\test" isDirectory: true]; + OTAssertEqualObjects(IRI.host, @"test"); + OTAssertEqualObjects(IRI.path, @"/"); + OTAssertEqualObjects(IRI.string, @"file://test/"); + OTAssertEqualObjects(IRI.fileSystemRepresentation, @"\\\\test"); +} # endif #endif - TEST(@"-[string]", - [IRI1.string isEqual: IRIString] && - [IRI2.string isEqual: @"http://foo:80"] && - [IRI3.string isEqual: @"http://bar/"] && - [IRI4.string isEqual: @"file:///etc/passwd"] && - [IRI5.string isEqual: @"http://foo/bar/qux/foo%2fbar"] && - [IRI6.string isEqual: @"https://[12:34::56:abcd]/"] && - [IRI7.string isEqual: @"https://[12:34::56:abcd]:234/"] && - [IRI8.string isEqual: @"urn:qux:foo"] && - [IRI9.string isEqual: @"file:/foo?query#frag"] && - [IRI10.string isEqual: @"file:foo@bar/qux?query#frag"] && - [IRI11.string isEqual: @"http://ä/ö?ü"]) - - TEST(@"-[scheme]", - [IRI1.scheme isEqual: @"ht+tp"] && [IRI4.scheme isEqual: @"file"] && - [IRI9.scheme isEqual: @"file"] && [IRI10.scheme isEqual: @"file"] && - [IRI11.scheme isEqual: @"http"]) - - TEST(@"-[user]", [IRI1.user isEqual: @"us:er"] && IRI4.user == nil && - IRI10.user == nil && IRI11.user == nil) - TEST(@"-[password]", - [IRI1.password isEqual: @"p@w"] && IRI4.password == nil && - IRI10.password == nil && IRI11.password == nil) - TEST(@"-[host]", [IRI1.host isEqual: @"ho:st"] && - [IRI6.host isEqual: @"12:34::56:abcd"] && - [IRI7.host isEqual: @"12:34::56:abcd"] && - IRI8.host == nil && IRI9.host == nil && IRI10.host == nil && - [IRI11.host isEqual: @"ä"]) - TEST(@"-[port]", IRI1.port.unsignedShortValue == 1234 && - [IRI4 port] == nil && IRI7.port.unsignedShortValue == 234 && - IRI8.port == nil && IRI9.port == nil && IRI10.port == nil && - IRI11.port == nil) - TEST(@"-[path]", - [IRI1.path isEqual: @"/pa?th"] && - [IRI4.path isEqual: @"/etc/passwd"] && - [IRI8.path isEqual: @"qux:foo"] && - [IRI9.path isEqual: @"/foo"] && - [IRI10.path isEqual: @"foo@bar/qux"] && - [IRI11.path isEqual: @"/ö"]) - TEST(@"-[pathComponents]", - [IRI1.pathComponents isEqual: - [OFArray arrayWithObjects: @"/", @"pa?th", nil]] && - [IRI4.pathComponents isEqual: - [OFArray arrayWithObjects: @"/", @"etc", @"passwd", nil]] && - [IRI5.pathComponents isEqual: - [OFArray arrayWithObjects: @"/", @"bar", @"qux", @"foo/bar", nil]]) - TEST(@"-[lastPathComponent]", - [[[OFIRI IRIWithString: @"http://host/foo//bar/baz"] - lastPathComponent] isEqual: @"baz"] && - [[[OFIRI IRIWithString: @"http://host/foo//bar/baz/"] - lastPathComponent] isEqual: @"baz"] && - [[[OFIRI IRIWithString: @"http://host/foo/"] - lastPathComponent] isEqual: @"foo"] && - [[[OFIRI IRIWithString: @"http://host/"] - lastPathComponent] isEqual: @"/"] && - [IRI5.lastPathComponent isEqual: @"foo/bar"]) - TEST(@"-[query]", - [IRI1.query isEqual: @"que#ry=1&f&oo=b=ar"] && IRI4.query == nil && - [IRI9.query isEqual: @"query"] && [IRI10.query isEqual: @"query"] && - [IRI11.query isEqual: @"ü"]) - TEST(@"-[queryItems]", - [IRI1.queryItems isEqual: [OFArray arrayWithObjects: +- (void)testString +{ + OTAssertEqualObjects(_IRI[0].string, IRI0String); + OTAssertEqualObjects(_IRI[1].string, @"http://foo:80"); + OTAssertEqualObjects(_IRI[2].string, @"http://bar/"); + OTAssertEqualObjects(_IRI[3].string, @"file:///etc/passwd"); + OTAssertEqualObjects(_IRI[4].string, @"http://foo/bar/qux/foo%2fbar"); + OTAssertEqualObjects(_IRI[5].string, @"https://[12:34::56:abcd]/"); + OTAssertEqualObjects(_IRI[6].string, @"https://[12:34::56:abcd]:234/"); + OTAssertEqualObjects(_IRI[7].string, @"urn:qux:foo"); + OTAssertEqualObjects(_IRI[8].string, @"file:/foo?query#frag"); + OTAssertEqualObjects(_IRI[9].string, @"file:foo@bar/qux?query#frag"); + OTAssertEqualObjects(_IRI[10].string, @"http://ä/ö?ü"); +} + +- (void)testScheme +{ + OTAssertEqualObjects(_IRI[0].scheme, @"ht+tp"); + OTAssertEqualObjects(_IRI[3].scheme, @"file"); + OTAssertEqualObjects(_IRI[8].scheme, @"file"); + OTAssertEqualObjects(_IRI[9].scheme, @"file"); + OTAssertEqualObjects(_IRI[10].scheme, @"http"); +} + +- (void)testUser +{ + OTAssertEqualObjects(_IRI[0].user, @"us:er"); + OTAssertNil(_IRI[3].user); + OTAssertNil(_IRI[9].user); + OTAssertNil(_IRI[10].user); +} + +- (void)testPassword +{ + OTAssertEqualObjects(_IRI[0].password, @"p@w"); + OTAssertNil(_IRI[3].password); + OTAssertNil(_IRI[9].password); + OTAssertNil(_IRI[10].password); +} + +- (void)testHost +{ + OTAssertEqualObjects(_IRI[0].host, @"ho:st"); + OTAssertEqualObjects(_IRI[5].host, @"12:34::56:abcd"); + OTAssertEqualObjects(_IRI[6].host, @"12:34::56:abcd"); + OTAssertNil(_IRI[7].host); + OTAssertNil(_IRI[8].host); + OTAssertNil(_IRI[9].host); + OTAssertEqualObjects(_IRI[10].host, @"ä"); +} + +- (void)testPort +{ + OTAssertEqual(_IRI[0].port.unsignedShortValue, 1234); + OTAssertNil(_IRI[3].port); + OTAssertEqual(_IRI[6].port.unsignedShortValue, 234); + OTAssertNil(_IRI[7].port); + OTAssertNil(_IRI[8].port); + OTAssertNil(_IRI[9].port); + OTAssertNil(_IRI[10].port); +} + +- (void)testPath +{ + OTAssertEqualObjects(_IRI[0].path, @"/pa?th"); + OTAssertEqualObjects(_IRI[3].path, @"/etc/passwd"); + OTAssertEqualObjects(_IRI[7].path, @"qux:foo"); + OTAssertEqualObjects(_IRI[8].path, @"/foo"); + OTAssertEqualObjects(_IRI[9].path, @"foo@bar/qux"); + OTAssertEqualObjects(_IRI[10].path, @"/ö"); +} + +- (void)testPathComponents +{ + OTAssertEqualObjects(_IRI[0].pathComponents, + ([OFArray arrayWithObjects: @"/", @"pa?th", nil])); + + OTAssertEqualObjects(_IRI[3].pathComponents, + ([OFArray arrayWithObjects: @"/", @"etc", @"passwd", nil])); + + OTAssertEqualObjects(_IRI[4].pathComponents, + ([OFArray arrayWithObjects: @"/", @"bar", @"qux", @"foo/bar", + nil])); +} + +- (void)testLastPathComponent +{ + OTAssertEqualObjects([[OFIRI IRIWithString: @"http://host/foo//bar/baz"] + lastPathComponent], + @"baz"); + + OTAssertEqualObjects( + [[OFIRI IRIWithString: @"http://host/foo//bar/baz/"] + lastPathComponent], + @"baz"); + + OTAssertEqualObjects([[OFIRI IRIWithString: @"http://host/foo/"] + lastPathComponent], + @"foo"); + + OTAssertEqualObjects([[OFIRI IRIWithString: @"http://host/"] + lastPathComponent], + @"/"); + + OTAssertEqualObjects(_IRI[4].lastPathComponent, @"foo/bar"); +} + +- (void)testQuery +{ + OTAssertEqualObjects(_IRI[0].query, @"que#ry=1&f&oo=b=ar"); + OTAssertNil(_IRI[3].query); + OTAssertEqualObjects(_IRI[8].query, @"query"); + OTAssertEqualObjects(_IRI[9].query, @"query"); + OTAssertEqualObjects(_IRI[10].query, @"ü"); +} + +- (void)testQueryItems +{ + OTAssertEqualObjects(_IRI[0].queryItems, + ([OFArray arrayWithObjects: [OFPair pairWithFirstObject: @"que#ry" secondObject: @"1"], - [OFPair pairWithFirstObject: @"f&oo" secondObject: @"b=ar"], nil]]); - TEST(@"-[fragment]", - [IRI1.fragment isEqual: @"frag#ment"] && IRI4.fragment == nil && - [IRI9.fragment isEqual: @"frag"] && - [IRI10.fragment isEqual: @"frag"]) - - TEST(@"-[copy]", R(IRI4 = [[IRI1 copy] autorelease])) - - TEST(@"-[isEqual:]", [IRI1 isEqual: IRI4] && ![IRI2 isEqual: IRI3] && - [[OFIRI IRIWithString: @"HTTP://bar/"] isEqual: IRI3]) - - TEST(@"-[hash:]", IRI1.hash == IRI4.hash && IRI2.hash != IRI3.hash) - - EXPECT_EXCEPTION(@"Detection of invalid format", - OFInvalidFormatException, [OFIRI IRIWithString: @"http"]) - - TEST(@"-[IRIByAddingPercentEncodingForUnicodeCharacters]", - [IRI11.IRIByAddingPercentEncodingForUnicodeCharacters - isEqual: [OFIRI IRIWithString: @"http://%C3%A4/%C3%B6?%C3%BC"]]) - - mutableIRI = [OFMutableIRI IRIWithScheme: @"dummy"]; - - EXPECT_EXCEPTION( - @"-[setPercentEncodedScheme:] with invalid characters fails", - OFInvalidFormatException, mutableIRI.scheme = @"%20") - - TEST(@"-[setHost:]", - (mutableIRI.host = @"ho:st") && - [mutableIRI.percentEncodedHost isEqual: @"ho%3Ast"] && - (mutableIRI.host = @"12:34:ab") && - [mutableIRI.percentEncodedHost isEqual: @"[12:34:ab]"] && - (mutableIRI.host = @"12:34:aB") && - [mutableIRI.percentEncodedHost isEqual: @"[12:34:aB]"] && - (mutableIRI.host = @"12:34:g") && - [mutableIRI.percentEncodedHost isEqual: @"12%3A34%3Ag"]) - - TEST(@"-[setPercentEncodedHost:]", - (mutableIRI.percentEncodedHost = @"ho%3Ast") && - [mutableIRI.host isEqual: @"ho:st"] && - (mutableIRI.percentEncodedHost = @"[12:34]") && - [mutableIRI.host isEqual: @"12:34"] && - (mutableIRI.percentEncodedHost = @"[12::ab]") && - [mutableIRI.host isEqual: @"12::ab"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedHost:] with invalid characters fails #1", - OFInvalidFormatException, - mutableIRI.percentEncodedHost = @"/") - - EXPECT_EXCEPTION( - @"-[setPercentEncodedHost:] with invalid characters fails #2", - OFInvalidFormatException, - mutableIRI.percentEncodedHost = @"[12:34") - - EXPECT_EXCEPTION( - @"-[setPercentEncodedHost:] with invalid characters fails #3", - OFInvalidFormatException, - mutableIRI.percentEncodedHost = @"[a::g]") - - TEST(@"-[setUser:]", - (mutableIRI.user = @"us:er") && - [mutableIRI.percentEncodedUser isEqual: @"us%3Aer"]) - - TEST(@"-[setPercentEncodedUser:]", - (mutableIRI.percentEncodedUser = @"us%3Aer") && - [mutableIRI.user isEqual: @"us:er"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedUser:] with invalid characters fails", - OFInvalidFormatException, - mutableIRI.percentEncodedHost = @"/") - - TEST(@"-[setPassword:]", - (mutableIRI.password = @"pass:word") && - [mutableIRI.percentEncodedPassword isEqual: @"pass%3Aword"]) - - TEST(@"-[setPercentEncodedPassword:]", - (mutableIRI.percentEncodedPassword = @"pass%3Aword") && - [mutableIRI.password isEqual: @"pass:word"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedPassword:] with invalid characters fails", - OFInvalidFormatException, - mutableIRI.percentEncodedPassword = @"/") - - TEST(@"-[setPath:]", - (mutableIRI.path = @"pa/th@?") && - [mutableIRI.percentEncodedPath isEqual: @"pa/th@%3F"]) - - TEST(@"-[setPercentEncodedPath:]", - (mutableIRI.percentEncodedPath = @"pa/th@%3F") && - [mutableIRI.path isEqual: @"pa/th@?"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedPath:] with invalid characters fails", - OFInvalidFormatException, - mutableIRI.percentEncodedPath = @"?") - - TEST(@"-[setQuery:]", - (mutableIRI.query = @"que/ry?#") && - [mutableIRI.percentEncodedQuery isEqual: @"que/ry?%23"]) - - TEST(@"-[setPercentEncodedQuery:]", - (mutableIRI.percentEncodedQuery = @"que/ry?%23") && - [mutableIRI.query isEqual: @"que/ry?#"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedQuery:] with invalid characters fails", - OFInvalidFormatException, - mutableIRI.percentEncodedQuery = @"`") - - TEST(@"-[setQueryItems:]", - (mutableIRI.queryItems = [OFArray arrayWithObjects: + [OFPair pairWithFirstObject: @"f&oo" secondObject: @"b=ar"], nil])); +} + +- (void)testFragment +{ + OTAssertEqualObjects(_IRI[0].fragment, @"frag#ment"); + OTAssertNil(_IRI[3].fragment); + OTAssertEqualObjects(_IRI[8].fragment, @"frag"); + OTAssertEqualObjects(_IRI[9].fragment, @"frag"); +} + +- (void)testCopy +{ + OTAssertEqualObjects([[_IRI[0] copy] autorelease], _IRI[0]); +} + +- (void)testIsEqual +{ + OTAssertEqualObjects(_IRI[0], [OFIRI IRIWithString: IRI0String]); + OTAssertNotEqualObjects(_IRI[1], _IRI[2]); + OTAssertEqualObjects([OFIRI IRIWithString: @"HTTP://bar/"], _IRI[2]); +} + +- (void)testHash +{ + OTAssertEqual(_IRI[0].hash, [[OFIRI IRIWithString: IRI0String] hash]); + OTAssertNotEqual(_IRI[1].hash, _IRI[2].hash); +} + +- (void)testIRIWithStringFailsWithInvalidFormat +{ + OTAssertThrowsSpecific([OFIRI IRIWithString: @"http"], + OFInvalidFormatException); +} + +- (void)testIRIByAddingPercentEncodingForUnicodeCharacters +{ + OTAssertEqualObjects( + _IRI[10].IRIByAddingPercentEncodingForUnicodeCharacters, + [OFIRI IRIWithString: @"http://%C3%A4/%C3%B6?%C3%BC"]); +} + +- (void)testSetPercentEncodedSchemeFailsWithInvalidCharacters +{ + OTAssertThrowsSpecific(_mutableIRI.scheme = @"%20", + OFInvalidFormatException); +} + +- (void)testSetHost +{ + _mutableIRI.host = @"ho:st"; + OTAssertEqualObjects(_mutableIRI.percentEncodedHost, @"ho%3Ast"); + + _mutableIRI.host = @"12:34:ab"; + OTAssertEqualObjects(_mutableIRI.percentEncodedHost, @"[12:34:ab]"); + + _mutableIRI.host = @"12:34:aB"; + OTAssertEqualObjects(_mutableIRI.percentEncodedHost, @"[12:34:aB]"); + + _mutableIRI.host = @"12:34:g"; + OTAssertEqualObjects(_mutableIRI.percentEncodedHost, @"12%3A34%3Ag"); +} + +- (void)testSetPercentEncodedHost +{ + _mutableIRI.percentEncodedHost = @"ho%3Ast"; + OTAssertEqualObjects(_mutableIRI.host, @"ho:st"); + + _mutableIRI.percentEncodedHost = @"[12:34]"; + OTAssertEqualObjects(_mutableIRI.host, @"12:34"); + + _mutableIRI.percentEncodedHost = @"[12::ab]"; + OTAssertEqualObjects(_mutableIRI.host, @"12::ab"); +} + +- (void)testSetPercentEncodedHostFailsWithInvalidCharacters +{ + OTAssertThrowsSpecific(_mutableIRI.percentEncodedHost = @"/", + OFInvalidFormatException); + + OTAssertThrowsSpecific(_mutableIRI.percentEncodedHost = @"[12:34", + OFInvalidFormatException); + + OTAssertThrowsSpecific(_mutableIRI.percentEncodedHost = @"[a::g]", + OFInvalidFormatException); +} + +- (void)testSetUser +{ + _mutableIRI.user = @"us:er"; + OTAssertEqualObjects(_mutableIRI.percentEncodedUser, @"us%3Aer"); +} + +- (void)testSetPercentEncodedUser +{ + _mutableIRI.percentEncodedUser = @"us%3Aer"; + OTAssertEqualObjects(_mutableIRI.user, @"us:er"); +} + +- (void)testSetPercentEncodedUserFailsWithInvalidCharacters +{ + OTAssertThrowsSpecific(_mutableIRI.percentEncodedHost = @"/", + OFInvalidFormatException); +} + +- (void)testSetPassword +{ + _mutableIRI.password = @"pass:word"; + OTAssertEqualObjects(_mutableIRI.percentEncodedPassword, + @"pass%3Aword"); +} + +- (void)testSetPercentEncodedPassword +{ + _mutableIRI.percentEncodedPassword = @"pass%3Aword"; + OTAssertEqualObjects(_mutableIRI.password, @"pass:word"); +} + +- (void)testSetPercentEncodedPasswordFailsWithInvalidCharacters +{ + OTAssertThrowsSpecific(_mutableIRI.percentEncodedPassword = @"/", + OFInvalidFormatException); +} + +- (void)testSetPath +{ + _mutableIRI.path = @"pa/th@?"; + OTAssertEqualObjects(_mutableIRI.percentEncodedPath, @"pa/th@%3F"); +} + +- (void)testSetPercentEncodedPath +{ + _mutableIRI.percentEncodedPath = @"pa/th@%3F"; + OTAssertEqualObjects(_mutableIRI.path, @"pa/th@?"); +} + +- (void)testSetPercentEncodedPathFailsWithInvalidCharacters +{ + OTAssertThrowsSpecific(_mutableIRI.percentEncodedPath = @"?", + OFInvalidFormatException); +} + +- (void)testSetQuery +{ + _mutableIRI.query = @"que/ry?#"; + OTAssertEqualObjects(_mutableIRI.percentEncodedQuery, @"que/ry?%23"); +} + +- (void)testSetPercentEncodedQuery +{ + _mutableIRI.percentEncodedQuery = @"que/ry?%23"; + OTAssertEqualObjects(_mutableIRI.query, @"que/ry?#"); +} + +- (void)testSetPercentEncodedQueryFailsWithInvalidCharacters +{ + OTAssertThrowsSpecific(_mutableIRI.percentEncodedQuery = @"`", + OFInvalidFormatException); +} + +- (void)testSetQueryItems +{ + _mutableIRI.queryItems = [OFArray arrayWithObjects: [OFPair pairWithFirstObject: @"foo&bar" secondObject: @"baz=qux"], [OFPair pairWithFirstObject: @"f=oobar" secondObject: @"b&azqux"], - nil]) && [mutableIRI.percentEncodedQuery isEqual: - @"foo%26bar=baz%3Dqux&f%3Doobar=b%26azqux"]) - - TEST(@"-[setFragment:]", - (mutableIRI.fragment = @"frag/ment?#") && - [mutableIRI.percentEncodedFragment isEqual: @"frag/ment?%23"]) - - TEST(@"-[setPercentEncodedFragment:]", - (mutableIRI.percentEncodedFragment = @"frag/ment?%23") && - [mutableIRI.fragment isEqual: @"frag/ment?#"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedFragment:] with invalid characters fails", - OFInvalidFormatException, - mutableIRI.percentEncodedFragment = @"`") - - TEST(@"-[IRIByAppendingPathComponent:isDirectory:]", - [[[OFIRI IRIWithString: @"file:///foo/bar"] - IRIByAppendingPathComponent: @"qux" isDirectory: false] isEqual: - [OFIRI IRIWithString: @"file:///foo/bar/qux"]] && - [[[OFIRI IRIWithString: @"file:///foo/bar/"] - IRIByAppendingPathComponent: @"qux" isDirectory: false] isEqual: - [OFIRI IRIWithString: @"file:///foo/bar/qux"]] && - [[[OFIRI IRIWithString: @"file:///foo/bar/"] - IRIByAppendingPathComponent: @"qu?x" isDirectory: false] isEqual: - [OFIRI IRIWithString: @"file:///foo/bar/qu%3Fx"]] && - [[[OFIRI IRIWithString: @"file:///foo/bar/"] - IRIByAppendingPathComponent: @"qu?x" isDirectory: true] isEqual: - [OFIRI IRIWithString: @"file:///foo/bar/qu%3Fx/"]]) - - TEST(@"-[IRIByStandardizingPath]", - [[[OFIRI IRIWithString: @"http://foo/bar/.."] - IRIByStandardizingPath] isEqual: - [OFIRI IRIWithString: @"http://foo/"]] && - [[[OFIRI IRIWithString: @"http://foo/bar/%2E%2E/../qux/"] - IRIByStandardizingPath] isEqual: - [OFIRI IRIWithString: @"http://foo/bar/qux/"]] && - [[[OFIRI IRIWithString: @"http://foo/bar/./././qux/./"] - IRIByStandardizingPath] isEqual: - [OFIRI IRIWithString: @"http://foo/bar/qux/"]] && - [[[OFIRI IRIWithString: @"http://foo/bar/../../qux"] - IRIByStandardizingPath] isEqual: - [OFIRI IRIWithString: @"http://foo/../qux"]]) - - objc_autoreleasePoolPop(pool); + nil]; + OTAssertEqualObjects(_mutableIRI.percentEncodedQuery, + @"foo%26bar=baz%3Dqux&f%3Doobar=b%26azqux"); +} + +- (void)testSetFragment +{ + _mutableIRI.fragment = @"frag/ment?#"; + OTAssertEqualObjects(_mutableIRI.percentEncodedFragment, + @"frag/ment?%23"); +} + +- (void)testSetPercentEncodedFragment +{ + _mutableIRI.percentEncodedFragment = @"frag/ment?%23"; + OTAssertEqualObjects(_mutableIRI.fragment, @"frag/ment?#"); +} + +- (void)testSetPercentEncodedFragmentFailsWithInvalidCharacters +{ + OTAssertThrowsSpecific(_mutableIRI.percentEncodedFragment = @"`", + OFInvalidFormatException); +} + +-(void)testIRIByAppendingPathComponentIsDirectory +{ + OTAssertEqualObjects([[OFIRI IRIWithString: @"file:///foo/bar"] + IRIByAppendingPathComponent: @"qux" + isDirectory: false], + [OFIRI IRIWithString: @"file:///foo/bar/qux"]); + + OTAssertEqualObjects([[OFIRI IRIWithString: @"file:///foo/bar/"] + IRIByAppendingPathComponent: @"qux" + isDirectory: false], + [OFIRI IRIWithString: @"file:///foo/bar/qux"]); + + OTAssertEqualObjects([[OFIRI IRIWithString: @"file:///foo/bar/"] + IRIByAppendingPathComponent: @"qu?x" + isDirectory: false], + [OFIRI IRIWithString: @"file:///foo/bar/qu%3Fx"]); + + OTAssertEqualObjects([[OFIRI IRIWithString: @"file:///foo/bar/"] + IRIByAppendingPathComponent: @"qu?x" + isDirectory: true], + [OFIRI IRIWithString: @"file:///foo/bar/qu%3Fx/"]); +} + +- (void)testIRIByStandardizingPath +{ + OTAssertEqualObjects([[OFIRI IRIWithString: @"http://foo/bar/.."] + IRIByStandardizingPath], + [OFIRI IRIWithString: @"http://foo/"]); + + OTAssertEqualObjects( + [[OFIRI IRIWithString: @"http://foo/bar/%2E%2E/../qux/"] + IRIByStandardizingPath], + [OFIRI IRIWithString: @"http://foo/bar/qux/"]); + + OTAssertEqualObjects( + [[OFIRI IRIWithString: @"http://foo/bar/./././qux/./"] + IRIByStandardizingPath], + [OFIRI IRIWithString: @"http://foo/bar/qux/"]); + + OTAssertEqualObjects([[OFIRI IRIWithString: @"http://foo/bar/../../qux"] + IRIByStandardizingPath], + [OFIRI IRIWithString: @"http://foo/../qux"]); } @end Index: tests/OFInvocationTests.m ================================================================== --- tests/OFInvocationTests.m +++ tests/OFInvocationTests.m @@ -19,77 +19,92 @@ #if defined(HAVE_COMPLEX_H) && !defined(__STDC_NO_COMPLEX__) # include #endif -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFInvocation"; +@interface OFInvocationTests: OTTestCase +{ + OFInvocation *_invocation; +} +@end struct TestStruct { unsigned char c; unsigned int i; }; -@implementation TestsAppDelegate (OFInvocationTests) +@implementation OFInvocationTests - (struct TestStruct)invocationTestMethod1: (unsigned char)c : (unsigned int)i - : (struct TestStruct *)ptr - : (struct TestStruct)st + : (struct TestStruct *)testStructPtr + : (struct TestStruct)testStruct +{ + return testStruct; +} + +- (void)setUp +{ + [super setUp]; + + SEL selector = @selector(invocationTestMethod1::::); + OFMethodSignature *signature = + [self methodSignatureForSelector: selector]; + + _invocation = [[OFInvocation alloc] initWithMethodSignature: signature]; +} + +- (void)dealloc +{ + [_invocation release]; + + [super dealloc]; +} + +- (void)testSetAndGetReturnValue { - return st; + struct TestStruct testStruct, testStruct2; + + memset(&testStruct, 0xFF, sizeof(testStruct)); + testStruct.c = 0x55; + testStruct.i = 0xAAAAAAAA; + + [_invocation setReturnValue: &testStruct]; + [_invocation getReturnValue: &testStruct2]; + OTAssertEqual(memcmp(&testStruct, &testStruct2, sizeof(testStruct)), 0); } -- (void)invocationTests +- (void)testSetAndGetArgumentAtIndex { - void *pool = objc_autoreleasePoolPush(); - SEL selector = @selector(invocationTestMethod1::::); - OFMethodSignature *sig = [self methodSignatureForSelector: selector]; - OFInvocation *invocation; - struct TestStruct st, st2, *stp = &st, *stp2; + struct TestStruct testStruct, testStruct2; + struct TestStruct *testStructPtr = &testStruct, *testStructPtr2; unsigned const char c = 0xAA; unsigned char c2; const unsigned int i = 0x55555555; unsigned int i2; - memset(&st, '\xFF', sizeof(st)); - st.c = 0x55; - st.i = 0xAAAAAAAA; - - TEST(@"+[invocationWithMethodSignature:]", - (invocation = [OFInvocation invocationWithMethodSignature: sig])) - - TEST(@"-[setReturnValue]", R([invocation setReturnValue: &st])) - - TEST(@"-[getReturnValue]", R([invocation getReturnValue: &st2]) && - memcmp(&st, &st2, sizeof(st)) == 0) - - memset(&st2, '\0', sizeof(st2)); - - TEST(@"-[setArgument:atIndex:] #1", - R([invocation setArgument: &c atIndex: 2])) - - TEST(@"-[setArgument:atIndex:] #2", - R([invocation setArgument: &i atIndex: 3])) - - TEST(@"-[setArgument:atIndex:] #3", - R([invocation setArgument: &stp atIndex: 4])) - - TEST(@"-[setArgument:atIndex:] #4", - R([invocation setArgument: &st atIndex: 5])) - - TEST(@"-[getArgument:atIndex:] #1", - R([invocation getArgument: &c2 atIndex: 2]) && c == c2) - - TEST(@"-[getArgument:atIndex:] #2", - R([invocation getArgument: &i2 atIndex: 3]) && i == i2) - - TEST(@"-[getArgument:atIndex:] #3", - R([invocation getArgument: &stp2 atIndex: 4]) && stp == stp2) - - TEST(@"-[getArgument:atIndex:] #4", - R([invocation getArgument: &st2 atIndex: 5]) && - memcmp(&st, &st2, sizeof(st)) == 0) - - objc_autoreleasePoolPop(pool); + memset(&testStruct, 0xFF, sizeof(testStruct)); + testStruct.c = 0x55; + testStruct.i = 0xAAAAAAAA; + + memset(&testStruct2, 0, sizeof(testStruct2)); + + [_invocation setArgument: &c atIndex: 2]; + [_invocation setArgument: &i atIndex: 3]; + [_invocation setArgument: &testStructPtr atIndex: 4]; + [_invocation setArgument: &testStruct atIndex: 5]; + + [_invocation getArgument: &c2 atIndex: 2]; + OTAssertEqual(c, c2); + + [_invocation getArgument: &i2 atIndex: 3]; + OTAssertEqual(i, i2); + + [_invocation getArgument: &testStructPtr2 atIndex: 4]; + OTAssertEqual(testStructPtr, testStructPtr2); + + [_invocation getArgument: &testStruct2 atIndex: 5]; + OTAssertEqual(memcmp(&testStruct, &testStruct2, sizeof(testStruct)), 0); } @end Index: tests/OFJSONTests.m ================================================================== --- tests/OFJSONTests.m +++ tests/OFJSONTests.m @@ -13,61 +13,93 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFJSON"; - -@implementation TestsAppDelegate (JSONTests) -- (void)JSONTests -{ - void *pool = objc_autoreleasePoolPush(); - OFString *string = @"{\"foo\"\t:'b\\na\\r', \"x\":/*foo*/ [.5\r,0xF," - @"null//bar\n,\"foo\",false]}"; - OFDictionary *dict = [OFDictionary dictionaryWithKeysAndObjects: +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFJSONTests: OTTestCase +{ + OFDictionary *_dictionary; +} +@end + +static OFString *string = @"{\"foo\"\t:'b\\na\\r', \"x\":/*foo*/ [.5\r,0xF," + @"null//bar\n,\"foo\",false]}"; + +@implementation OFJSONTests +- (void)setUp +{ + [super setUp]; + + _dictionary = [[OTOrderedDictionary alloc] initWithKeysAndObjects: @"foo", @"b\na\r", @"x", [OFArray arrayWithObjects: [OFNumber numberWithFloat: .5f], [OFNumber numberWithInt: 0xF], [OFNull null], @"foo", [OFNumber numberWithBool: false], nil], nil]; - - TEST(@"-[objectByParsingJSON] #1", - [string.objectByParsingJSON isEqual: dict]) - - TEST(@"-[JSONRepresentation]", - [[dict JSONRepresentation] isEqual: - @"{\"x\":[0.5,15,null,\"foo\",false],\"foo\":\"b\\na\\r\"}"]) - - TEST(@"OFJSONRepresentationOptionPretty", - [[dict JSONRepresentationWithOptions: - OFJSONRepresentationOptionPretty] isEqual: - @"{\n\t\"x\": [\n\t\t0.5,\n\t\t15,\n\t\tnull,\n\t\t" - @"\"foo\",\n\t\tfalse\n\t],\n\t\"foo\": \"b\\na\\r\"\n}"]) - - TEST(@"OFJSONRepresentationOptionJSON5", - [[dict JSONRepresentationWithOptions: - OFJSONRepresentationOptionJSON5] isEqual: - @"{x:[0.5,15,null,\"foo\",false],foo:\"b\\\na\\r\"}"]) - - EXPECT_EXCEPTION(@"-[objectByParsingJSON] #2", OFInvalidJSONException, - [@"{" objectByParsingJSON]) - EXPECT_EXCEPTION(@"-[objectByParsingJSON] #3", OFInvalidJSONException, - [@"]" objectByParsingJSON]) - EXPECT_EXCEPTION(@"-[objectByParsingJSON] #4", OFInvalidJSONException, - [@"bar" objectByParsingJSON]) - EXPECT_EXCEPTION(@"-[objectByParsingJSON] #5", OFInvalidJSONException, - [@"[\"a\" \"b\"]" objectByParsingJSON]) - - TEST(@"-[objectByParsingJSON] #6", - [@"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" - .objectByParsingJSON isEqual: [OFArray arrayWithObject: +} + +- (void)dealloc +{ + [_dictionary release]; + + [super dealloc]; +} + +- (void)testObjectByParsingJSON +{ + OTAssertEqualObjects(string.objectByParsingJSON, _dictionary); +} + +- (void)testJSONRepresentation +{ + OTAssert(_dictionary.JSONRepresentation, + @"{\"foo\":\"b\\na\\r\",\"x\":[0.5,15,null,\"foo\",false]}"); +} + +- (void)testPrettyJSONRepresentation +{ + OTAssertEqualObjects([_dictionary JSONRepresentationWithOptions: + OFJSONRepresentationOptionPretty], + @"{\n\t\"foo\": \"b\\na\\r\",\n\t\"x\": [\n\t\t0.5,\n\t\t15," + @"\n\t\tnull,\n\t\t\"foo\",\n\t\tfalse\n\t]\n}"); +} + +- (void)testJSON5Representation +{ + OTAssertEqualObjects([_dictionary JSONRepresentationWithOptions: + OFJSONRepresentationOptionJSON5], + @"{foo:\"b\\\na\\r\",x:[0.5,15,null,\"foo\",false]}"); +} + +- (void)testObjectByParsingJSONFailsWithInvalidJSON +{ + OTAssertThrowsSpecific([@"{" objectByParsingJSON], + OFInvalidJSONException); + + OTAssertThrowsSpecific([@"]" objectByParsingJSON], + OFInvalidJSONException); + + OTAssertThrowsSpecific([@"bar" objectByParsingJSON], + OFInvalidJSONException); + + OTAssertThrowsSpecific([@"[\"a\" \"b\"]" objectByParsingJSON], + OFInvalidJSONException); +} + +- (void)testObjectByParsingJSONWithDeepNesting +{ + OTAssertEqualObjects( + @"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + .objectByParsingJSON, + [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: @@ -79,14 +111,16 @@ [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: [OFArray arrayWithObject: - [OFDictionary dictionary]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]) + [OFDictionary dictionary]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]); +} - EXPECT_EXCEPTION(@"-[objectByParsingJSON] #7", OFInvalidJSONException, +- (void)testObjectByParsingJSONFailsWithTooDeepNesting +{ + OTAssertThrowsSpecific( [@"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" - objectByParsingJSON]) - - objc_autoreleasePoolPop(pool); + objectByParsingJSON], + OFInvalidJSONException); } @end Index: tests/OFKernelEventObserverTests.m ================================================================== --- tests/OFKernelEventObserverTests.m +++ tests/OFKernelEventObserverTests.m @@ -13,10 +13,13 @@ * file. */ #include "config.h" +#import "ObjFW.h" +#import "ObjFWTest.h" + #ifdef HAVE_KQUEUE # import "OFKqueueKernelEventObserver.h" #endif #ifdef HAVE_EPOLL # import "OFEpollKernelEventObserver.h" @@ -26,211 +29,127 @@ #endif #ifdef HAVE_SELECT # import "OFSelectKernelEventObserver.h" #endif -#import "TestsAppDelegate.h" +@interface OFKernelEventObserverTests: OTTestCase + +{ + OFTCPSocket *_server, *_client, *_accepted; + OFKernelEventObserver *_observer; + size_t _events; +} +@end static const size_t numExpectedEvents = 3; -static OFString *module; - -@interface ObserverTest: OFObject -{ -@public - TestsAppDelegate *_testsAppDelegate; - OFKernelEventObserver *_observer; - OFTCPSocket *_server, *_client, *_accepted; - size_t _events; - int _fails; -} - -- (void)run; -@end - -@implementation ObserverTest -- (instancetype)initWithTestsAppDelegate: (TestsAppDelegate *)testsAppDelegate -{ - self = [super init]; - - @try { - OFSocketAddress address; - - _testsAppDelegate = testsAppDelegate; - - _server = [[OFTCPSocket alloc] init]; - address = [_server bindToHost: @"127.0.0.1" port: 0]; - [_server listen]; - - _client = [[OFTCPSocket alloc] init]; - [_client connectToHost: @"127.0.0.1" - port: OFSocketAddressIPPort(&address)]; - [_client writeBuffer: "0" length: 1]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; +@implementation OFKernelEventObserverTests +- (void)setUp +{ + OFSocketAddress address; + + [super setUp]; + + _server = [[OFTCPSocket alloc] init]; + address = [_server bindToHost: @"127.0.0.1" port: 0]; + [_server listen]; + + _client = [[OFTCPSocket alloc] init]; + [_client connectToHost: @"127.0.0.1" + port: OFSocketAddressIPPort(&address)]; + [_client writeBuffer: "0" length: 1]; } - (void)dealloc { - [_server release]; [_client release]; + [_server release]; [_accepted release]; [super dealloc]; } -- (void)run +- (void)testKernelEventObserverWithClass: (Class)class { - OFDate *deadline; bool deadlineExceeded = false; + OFDate *deadline; - [_testsAppDelegate outputTesting: @"-[observe] with listening socket" - inModule: module]; + _observer = [[class alloc] init]; + _observer.delegate = self; + [_observer addObjectForReading: _server]; deadline = [OFDate dateWithTimeIntervalSinceNow: 1]; + while (_events < numExpectedEvents) { if (deadline.timeIntervalSinceNow < 0) { deadlineExceeded = true; break; } [_observer observeForTimeInterval: 0.01]; } - if (!deadlineExceeded) - [_testsAppDelegate - outputSuccess: @"-[observe] not exceeding deadline" - inModule: module]; - else { - [_testsAppDelegate - outputFailure: @"-[observe] not exceeding deadline" - inModule: module]; - _fails++; - } - - if (_events == numExpectedEvents) - [_testsAppDelegate - outputSuccess: @"-[observe] handling all events" - inModule: module]; - else { - [_testsAppDelegate - outputFailure: @"-[observe] handling all events" - inModule: module]; - _fails++; - } + OTAssertFalse(deadlineExceeded); + OTAssertEqual(_events, numExpectedEvents); } - (void)objectIsReadyForReading: (id)object { char buffer; switch (_events++) { case 0: - if (object == _server) - [_testsAppDelegate - outputSuccess: @"-[observe] with listening socket" - inModule: module]; - else { - [_testsAppDelegate - outputFailure: @"-[observe] with listening socket" - inModule: module]; - _fails++; - } + OTAssertEqual(object, _server); _accepted = [[object accept] retain]; [_observer addObjectForReading: _accepted]; - - [_testsAppDelegate - outputTesting: @"-[observe] with data ready to read" - inModule: module]; - break; case 1: - if (object == _accepted && - [object readIntoBuffer: &buffer length: 1] == 1 && - buffer == '0') - [_testsAppDelegate - outputSuccess: @"-[observe] with data ready to read" - inModule: module]; - else { - [_testsAppDelegate - outputFailure: @"-[observe] with data ready to read" - inModule: module]; - _fails++; - } + OTAssert(object, _accepted); + + OTAssertEqual([object readIntoBuffer: &buffer length: 1], 1); + OTAssertEqual(buffer, '0'); [_client close]; - - [_testsAppDelegate - outputTesting: @"-[observe] with closed connection" - inModule: module]; - break; case 2: - if (object == _accepted && - [object readIntoBuffer: &buffer length: 1] == 0) - [_testsAppDelegate - outputSuccess: @"-[observe] with closed connection" - inModule: module]; - else { - [_testsAppDelegate - outputFailure: @"-[observe] with closed connection" - inModule: module]; - _fails++; - } - - break; - default: - OFEnsure(0); - } -} -@end - -@implementation TestsAppDelegate (OFKernelEventObserverTests) -- (void)kernelEventObserverTestsWithClass: (Class)class -{ - void *pool = objc_autoreleasePoolPush(); - ObserverTest *test; - - module = [class className]; - test = [[[ObserverTest alloc] - initWithTestsAppDelegate: self] autorelease]; - - TEST(@"+[observer]", (test->_observer = [class observer])) - test->_observer.delegate = test; - - TEST(@"-[addObjectForReading:]", - R([test->_observer addObjectForReading: test->_server])) - - [test run]; - _fails += test->_fails; - - objc_autoreleasePoolPop(pool); -} - -- (void)kernelEventObserverTests -{ -#ifdef HAVE_SELECT - [self kernelEventObserverTestsWithClass: - [OFSelectKernelEventObserver class]]; + OTAssertEqual(object, _accepted); + + OTAssertEqual([object readIntoBuffer: &buffer length: 1], 0); + break; + default: + OTAssert(false); + } +} + +#ifdef HAVE_SELECT +- (void)testSelectKernelEventObserver +{ + [self testKernelEventObserverWithClass: + [OFSelectKernelEventObserver class]]; +} #endif #ifdef HAVE_POLL - [self kernelEventObserverTestsWithClass: +- (void)testPollKernelEventObserver +{ + [self testKernelEventObserverWithClass: [OFPollKernelEventObserver class]]; +} #endif #ifdef HAVE_EPOLL - [self kernelEventObserverTestsWithClass: +- (void)testEpollKernelEventObserver +{ + [self testKernelEventObserverWithClass: [OFEpollKernelEventObserver class]]; +} #endif #ifdef HAVE_KQUEUE - [self kernelEventObserverTestsWithClass: +- (void)testKqueueKernelEventObserver +{ + [self testKernelEventObserverWithClass: [OFKqueueKernelEventObserver class]]; -#endif } +#endif @end Index: tests/OFListTests.m ================================================================== --- tests/OFListTests.m +++ tests/OFListTests.m @@ -13,140 +13,265 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFList"; -static OFString *strings[] = { - @"Foo", - @"Bar", - @"Baz" -}; - -@implementation TestsAppDelegate (OFListTests) -- (void)listTests -{ - void *pool = objc_autoreleasePoolPush(); - OFList *list; - OFEnumerator *enumerator; - OFListItem iter; - OFString *object; - size_t i; - bool ok; - - TEST(@"+[list]", (list = [OFList list])) - - TEST(@"-[appendObject:]", [list appendObject: strings[0]] && - [list appendObject: strings[1]] && [list appendObject: strings[2]]) - - TEST(@"-[firstListItem]", - [OFListItemObject(list.firstListItem) isEqual: strings[0]]) - - TEST(@"OFListItemNext()", - [OFListItemObject(OFListItemNext(list.firstListItem)) - isEqual: strings[1]]) - - TEST(@"-[lastListItem]", - [OFListItemObject(list.lastListItem) isEqual: strings[2]]) - - TEST(@"OFListItemPrevious()", - [OFListItemObject(OFListItemPrevious(list.lastListItem)) - isEqual: strings[1]]) - - TEST(@"-[removeListItem:]", - R([list removeListItem: list.lastListItem]) && - [list.lastObject isEqual: strings[1]] && - R([list removeListItem: list.firstListItem]) && - [list.firstObject isEqual: list.lastObject]) - - TEST(@"-[insertObject:beforeListItem:]", - [list insertObject: strings[0] beforeListItem: list.lastListItem] && - [OFListItemObject(OFListItemPrevious(list.lastListItem)) - isEqual: strings[0]]) - - TEST(@"-[insertObject:afterListItem:]", - [list insertObject: strings[2] - afterListItem: OFListItemNext(list.firstListItem)] && - [list.lastObject isEqual: strings[2]]) - - TEST(@"-[count]", list.count == 3) - - TEST(@"-[containsObject:]", - [list containsObject: strings[1]] && - ![list containsObject: @"nonexistent"]) - - TEST(@"-[containsObjectIdenticalTo:]", - [list containsObjectIdenticalTo: strings[1]] && - ![list containsObjectIdenticalTo: - [OFString stringWithString: strings[1]]]) - - TEST(@"-[copy]", (list = [[list copy] autorelease]) && - [list.firstObject isEqual: strings[0]] && - [OFListItemObject(OFListItemNext(list.firstListItem)) - isEqual: strings[1]] && - [list.lastObject isEqual: strings[2]]) - - TEST(@"-[isEqual:]", [list isEqual: [[list copy] autorelease]]) - - TEST(@"-[description]", - [list.description isEqual: @"[\n\tFoo,\n\tBar,\n\tBaz\n]"]) - - TEST(@"-[objectEnumerator]", (enumerator = [list objectEnumerator])) - - iter = list.firstListItem; - i = 0; - ok = true; - while ((object = [enumerator nextObject]) != nil) { - if (![object isEqual: OFListItemObject(iter)]) - ok = false; - - iter = OFListItemNext(iter); - i++; - } - - if (list.count != i) - ok = false; - - TEST(@"OFEnumerator's -[nextObject]", ok); - - [list removeListItem: list.firstListItem]; - - EXPECT_EXCEPTION(@"Detection of mutation during enumeration", - OFEnumerationMutationException, [enumerator nextObject]) - - [list prependObject: strings[0]]; - - iter = list.firstListItem; - i = 0; - ok = true; - - for (OFString *object_ in list) { - if (![object_ isEqual: OFListItemObject(iter)]) - ok = false; - - iter = OFListItemNext(iter); - i++; - } - - if (list.count != i) - ok = false; - - TEST(@"Fast Enumeration", ok) - - ok = false; - @try { - for (OFString *object_ in list) { - (void)object_; - - [list removeListItem: list.lastListItem]; +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFListTests: OTTestCase +{ + OFList *_list; +} +@end + +@implementation OFListTests +- (void)setUp +{ + [super setUp]; + + _list = [[OFList alloc] init]; + [_list appendObject: @"Foo"]; + [_list appendObject: @"Bar"]; + [_list appendObject: @"Baz"]; +} + +- (void)dealloc +{ + [_list release]; + + [super dealloc]; +} + +- (void)testCount +{ + OTAssertEqual(_list.count, 3); +} + +- (void)testAppendObject +{ + OFListItem item; + + [_list appendObject: @"Qux"]; + + item = _list.firstListItem; + OTAssertEqualObjects(OFListItemObject(item), @"Foo"); + + item = OFListItemNext(item); + OTAssertEqualObjects(OFListItemObject(item), @"Bar"); + + item = OFListItemNext(item); + OTAssertEqualObjects(OFListItemObject(item), @"Baz"); + + item = OFListItemNext(item); + OTAssertEqualObjects(OFListItemObject(item), @"Qux"); + + item = OFListItemNext(item); + OTAssertEqual(item, NULL); +} + +- (void)testFirstListItem +{ + OTAssertEqualObjects(OFListItemObject(_list.firstListItem), @"Foo"); +} + +- (void)testFirstObject +{ + OTAssertEqualObjects(_list.firstObject, @"Foo"); +} + +- (void)testLastListItem +{ + OTAssertEqualObjects(OFListItemObject(_list.lastListItem), @"Baz"); +} + +- (void)testLastObject +{ + OTAssertEqualObjects(_list.lastObject, @"Baz"); +} + +- (void)testListItemNext +{ + OTAssertEqualObjects( + OFListItemObject(OFListItemNext(_list.firstListItem)), @"Bar"); +} + +- (void)testListItemPrevious +{ + OTAssertEqualObjects( + OFListItemObject(OFListItemPrevious(_list.lastListItem)), @"Bar"); +} + +- (void)testRemoveListItem +{ + OFListItem item; + + [_list removeListItem: OFListItemNext(_list.firstListItem)]; + + item = _list.firstListItem; + OTAssertEqualObjects(OFListItemObject(item), @"Foo"); + + item = OFListItemNext(item); + OTAssertEqualObjects(OFListItemObject(item), @"Baz"); + + item = OFListItemNext(item); + OTAssertEqual(item, NULL); +} + +- (void)testInsertObjectBeforeListItem +{ + OFListItem item; + + [_list insertObject: @"Qux" beforeListItem: _list.lastListItem]; + + item = _list.firstListItem; + OTAssertEqualObjects(OFListItemObject(item), @"Foo"); + + item = OFListItemNext(item); + OTAssertEqualObjects(OFListItemObject(item), @"Bar"); + + item = OFListItemNext(item); + OTAssertEqualObjects(OFListItemObject(item), @"Qux"); + + item = OFListItemNext(item); + OTAssertEqualObjects(OFListItemObject(item), @"Baz"); + + item = OFListItemNext(item); + OTAssertEqual(item, NULL); +} + +- (void)testInsertObjectAfterListItem +{ + OFListItem item; + + [_list insertObject: @"Qux" afterListItem: _list.firstListItem]; + + item = _list.firstListItem; + OTAssertEqualObjects(OFListItemObject(item), @"Foo"); + + item = OFListItemNext(item); + OTAssertEqualObjects(OFListItemObject(item), @"Qux"); + + item = OFListItemNext(item); + OTAssertEqualObjects(OFListItemObject(item), @"Bar"); + + item = OFListItemNext(item); + OTAssertEqualObjects(OFListItemObject(item), @"Baz"); + + item = OFListItemNext(item); + OTAssertEqual(item, NULL); +} + +- (void)testContainsObject +{ + OTAssertTrue([_list containsObject: @"Foo"]); + OTAssertFalse([_list containsObject: @"Qux"]); +} + +- (void)testContainsObjectIdenticalTo +{ + OFString *foo = _list.firstObject; + + OTAssertTrue([_list containsObjectIdenticalTo: foo]); + OTAssertFalse( + [_list containsObjectIdenticalTo: [[foo mutableCopy] autorelease]]); +} + +- (void)testIsEqual +{ + OFList *list = [OFList list]; + + [list appendObject: @"Foo"]; + [list appendObject: @"Bar"]; + [list appendObject: @"Baz"]; + + OTAssertEqualObjects(list, _list); + + [list appendObject: @"Qux"]; + + OTAssertNotEqualObjects(list, _list); +} + +- (void)testHash +{ + OFList *list = [OFList list]; + + [list appendObject: @"Foo"]; + [list appendObject: @"Bar"]; + [list appendObject: @"Baz"]; + + OTAssertEqual(list.hash, _list.hash); + + [list appendObject: @"Qux"]; + + OTAssertNotEqual(list.hash, _list.hash); +} + +- (void)testCopy +{ + OTAssertEqualObjects([[_list copy] autorelease], _list); +} + +- (void)testDescription +{ + OTAssertEqualObjects(_list.description, @"[\n\tFoo,\n\tBar,\n\tBaz\n]"); +} + +- (void)testEnumerator +{ + OFEnumerator *enumerator = [_list objectEnumerator]; + + OTAssertEqualObjects([enumerator nextObject], @"Foo"); + OTAssertEqualObjects([enumerator nextObject], @"Bar"); + OTAssertEqualObjects([enumerator nextObject], @"Baz"); + OTAssertNil([enumerator nextObject]); +} + +- (void)testDetectMutationDuringEnumeration +{ + OFEnumerator *enumerator = [_list objectEnumerator]; + + [_list removeListItem: _list.firstListItem]; + + OTAssertThrowsSpecific([enumerator nextObject], + OFEnumerationMutationException); +} + +- (void)testFastEnumeration +{ + size_t i = 0; + + for (OFString *object in _list) { + OTAssertLessThan(i, 3); + + switch (i++) { + case 0: + OTAssertEqualObjects(object, @"Foo"); + break; + case 1: + OTAssertEqualObjects(object, @"Bar"); + break; + case 2: + OTAssertEqualObjects(object, @"Baz"); + break; + } + } + + OTAssertEqual(i, 3); +} + +- (void)testDetectMutationDuringFastEnumeration +{ + bool detected = false; + + @try { + for (OFString *object in _list) { + (void)object; + [_list removeListItem: _list.firstListItem]; } } @catch (OFEnumerationMutationException *e) { - ok = true; + detected = true; } - TEST(@"Detection of mutation during Fast Enumeration", ok) - - objc_autoreleasePoolPop(pool); + OTAssertTrue(detected); } @end Index: tests/OFLocaleTests.m ================================================================== --- tests/OFLocaleTests.m +++ tests/OFLocaleTests.m @@ -13,29 +13,30 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -@implementation TestsAppDelegate (OFLocaleTests) -- (void)localeTests -{ - void *pool = objc_autoreleasePoolPush(); - - [OFStdOut setForegroundColor: [OFColor lime]]; - - [OFStdOut writeFormat: @"[OFLocale] Language code: %@\n", - [OFLocale languageCode]]; - - [OFStdOut writeFormat: @"[OFLocale] Country code: %@\n", - [OFLocale countryCode]]; - - [OFStdOut writeFormat: @"[OFLocale] Encoding: %@\n", - OFStringEncodingName([OFLocale encoding])]; - - [OFStdOut writeFormat: @"[OFLocale] Decimal separator: %@\n", - [OFLocale decimalSeparator]]; - - objc_autoreleasePoolPop(pool); +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFLocaleTests: OTTestCase +@end + +@implementation OFLocaleTests ++ (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, id) *) *)summary +{ + OFMutableArray *summary = [OFMutableArray array]; + +#define ADD(name, value) \ + [summary addObject: [OFPair pairWithFirstObject: name \ + secondObject: value]]; + + ADD(@"Language code", [OFLocale languageCode]) + ADD(@"Country code", [OFLocale countryCode]) + ADD(@"Encoding", OFStringEncodingName([OFLocale encoding])) + ADD(@"Decimal separator", [OFLocale decimalSeparator]) + +#undef ADD + + return summary; } @end DELETED tests/OFMD5HashTests.m Index: tests/OFMD5HashTests.m ================================================================== --- tests/OFMD5HashTests.m +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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" - -#include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFMD5Hash"; - -const uint8_t testFileMD5[16] = - "\x00\x8B\x9D\x1B\x58\xDF\xF8\xFE\xEE\xF3\xAE\x8D\xBB\x68\x2D\x38"; - -@implementation TestsAppDelegate (OFMD5HashTests) -- (void)MD5HashTests -{ - void *pool = objc_autoreleasePoolPush(); - OFMD5Hash *MD5, *MD5Copy; - OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; - - TEST(@"+[hashWithAllowsSwappableMemory:]", - (MD5 = [OFMD5Hash hashWithAllowsSwappableMemory: true])) - - while (!file.atEndOfStream) { - char buffer[64]; - size_t length = [file readIntoBuffer: buffer length: 64]; - [MD5 updateWithBuffer: buffer length: length]; - } - [file close]; - - TEST(@"-[copy]", (MD5Copy = [[MD5 copy] autorelease])) - - TEST(@"-[calculate]", R([MD5 calculate]) && R([MD5Copy calculate])) - - TEST(@"-[digest]", - memcmp(MD5.digest, testFileMD5, 16) == 0 && - memcmp(MD5Copy.digest, testFileMD5, 16) == 0) - - EXPECT_EXCEPTION(@"Detect invalid call of " - @"-[updateWithBuffer:length]", OFHashAlreadyCalculatedException, - [MD5 updateWithBuffer: "" length: 1]) - - objc_autoreleasePoolPop(pool); -} -@end Index: tests/OFMatrix4x4Tests.m ================================================================== --- tests/OFMatrix4x4Tests.m +++ tests/OFMatrix4x4Tests.m @@ -13,94 +13,153 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFMatrix4x4Tests"; - -@implementation TestsAppDelegate (OFMatrix4x4Tests) -- (void)matrix4x4Tests -{ - void *pool = objc_autoreleasePoolPush(); - OFMatrix4x4 *matrix, *matrix2; - OFVector4D point; - OFVector4D points[2] = {{ 1, 2, 3, 1 }, { 7, 8, 9, 2 }}; - - TEST(@"+[identityMatrix]", - memcmp([[OFMatrix4x4 identityMatrix] values], (const float [4][4]){ - { 1, 0, 0, 0 }, - { 0, 1, 0, 0 }, - { 0, 0, 1, 0 }, - { 0, 0, 0, 1 } - }, 16 * sizeof(float)) == 0) - - TEST(@"+[matrixWithValues:]", - (matrix = [OFMatrix4x4 matrixWithValues: (const float [4][4]){ +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFMatrix4x4Tests: OTTestCase +{ + OFMatrix4x4 *_matrix; +} +@end + +@implementation OFMatrix4x4Tests +- (void)setUp +{ + [super setUp]; + + _matrix = [[OFMatrix4x4 alloc] initWithValues: (const float [4][4]){ { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 16 } - }])) - - TEST(@"-[description]", - [matrix.description isEqual: @""]) - - TEST(@"-[isEqual:]", [[OFMatrix4x4 identityMatrix] isEqual: - [OFMatrix4x4 matrixWithValues: (const float [4][4]){ + }]; +} + +- (void)dealloc +{ + [_matrix release]; + + [super dealloc]; +} + +- (void)testIdentityMatrix +{ + OTAssertEqual(memcmp([[OFMatrix4x4 identityMatrix] values], + (const float [4][4]){ + { 1, 0, 0, 0 }, + { 0, 1, 0, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 1 } + }, 16 * sizeof(float)), + 0); +} + +- (void)testDescription +{ + OTAssertEqualObjects(_matrix.description, + @""); +} + +- (void)testIsEqual +{ + OTAssertEqualObjects([OFMatrix4x4 identityMatrix], + ([OFMatrix4x4 matrixWithValues: (const float [4][4]){ + { 1, 0, 0, 0 }, + { 0, 1, 0, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 1 } + }])); +} + +- (void)testHash +{ + OTAssertEqual([[OFMatrix4x4 identityMatrix] hash], + [([OFMatrix4x4 matrixWithValues: (const float [4][4]){ { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } - }]]) - - TEST(@"-[copy]", (matrix2 = [[matrix copy] autorelease]) && - [matrix2 isEqual: matrix]) - - TEST(@"-[multiplyWithMatrix:] #1", - R([matrix2 multiplyWithMatrix: [OFMatrix4x4 identityMatrix]]) && - [matrix2 isEqual: matrix]) - - matrix2 = [OFMatrix4x4 matrixWithValues: (const float [4][4]){ + }]) hash]); +} + +- (void)testCopy +{ + OTAssertEqualObjects([[_matrix copy] autorelease], _matrix); +} + +- (void)testMultiplyWithMatrix +{ + OFMatrix4x4 *matrix; + + matrix = [[_matrix copy] autorelease]; + [matrix multiplyWithMatrix: [OFMatrix4x4 identityMatrix]]; + OTAssertEqualObjects(matrix, _matrix); + + matrix = [OFMatrix4x4 matrixWithValues: (const float [4][4]){ { 100, 200, 300, 400 }, { 500, 600, 700, 800 }, { 900, 1000, 1100, 1200 }, { 1300, 1400, 1500, 1600 } }]; - TEST(@"-[multiplyWithMatrix:] #2", - R([matrix2 multiplyWithMatrix: matrix]) && - [matrix2 isEqual: - [OFMatrix4x4 matrixWithValues: (const float [4][4]){ + [matrix multiplyWithMatrix: _matrix]; + OTAssertEqualObjects(matrix, + ([OFMatrix4x4 matrixWithValues: (const float [4][4]){ { 9000, 10000, 11000, 12000 }, { 20200, 22800, 25400, 28000 }, { 31400, 35600, 39800, 44000 }, { 42600, 48400, 54200, 60000 } - }]]) - - TEST(@"-[translateWithVector:]", - (matrix2 = [OFMatrix4x4 identityMatrix]) && - R([matrix2 translateWithVector: OFMakeVector3D(1, 2, 3)]) && - R(point = - [matrix2 transformedVector: OFMakeVector4D(2, 3, 4, 1)]) && - point.x == 3 && point.y == 5 && point.z == 7 && point.w == 1) - - TEST(@"-[scaleWithVector:]", - R([matrix2 scaleWithVector: OFMakeVector3D(-1, 0.5f, 2)]) && - R(point = - [matrix2 transformedVector: OFMakeVector4D(2, 3, 4, 1)]) && - point.x == -3 && point.y == 2.5 && point.z == 14 && point.w == 1) - - TEST(@"-[transformVectors:count:]", - R([matrix transformVectors: points count: 2]) && - points[0].x == 18 && points[0].y == 46 && points[0].z == 74 && - points[0].w == 102 && points[1].x == 58 && points[1].y == 162 && - points[1].z == 266 && points[1].w == 370) - - objc_autoreleasePoolPop(pool); + }])); +} + +- (void)testTranslateWithVector +{ + OFMatrix4x4 *matrix = [OFMatrix4x4 identityMatrix]; + OFVector4D point; + + [matrix translateWithVector: OFMakeVector3D(1, 2, 3)]; + + point = [matrix transformedVector: OFMakeVector4D(2, 3, 4, 1)]; + OTAssertEqual(point.x, 3); + OTAssertEqual(point.y, 5); + OTAssertEqual(point.z, 7); + OTAssertEqual(point.w, 1); +} + +- (void)testScaleWithVector +{ + OFMatrix4x4 *matrix = [OFMatrix4x4 identityMatrix]; + OFVector4D point; + + [matrix translateWithVector: OFMakeVector3D(1, 2, 3)]; + [matrix scaleWithVector: OFMakeVector3D(-1, 0.5f, 2)]; + + point = [matrix transformedVector: OFMakeVector4D(2, 3, 4, 1)]; + OTAssertEqual(point.x, -3); + OTAssertEqual(point.y, 2.5); + OTAssertEqual(point.z, 14); + OTAssertEqual(point.w, 1); +} + +- (void)testTransformVectorsCount +{ + OFVector4D points[2] = {{ 1, 2, 3, 1 }, { 7, 8, 9, 2 }}; + + [_matrix transformVectors: points count: 2]; + + OTAssertEqual(points[0].x, 18); + OTAssertEqual(points[0].y, 46); + OTAssertEqual(points[0].z, 74); + OTAssertEqual(points[0].w, 102); + OTAssertEqual(points[1].x, 58); + OTAssertEqual(points[1].y, 162); + OTAssertEqual(points[1].z, 266); + OTAssertEqual(points[1].w, 370); } @end Index: tests/OFMemoryStreamTests.m ================================================================== --- tests/OFMemoryStreamTests.m +++ tests/OFMemoryStreamTests.m @@ -13,75 +13,73 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFMemoryStream"; +@interface OFMemoryStreamTests: OTTestCase +@end + static const char string[] = "abcdefghijkl"; -@implementation TestsAppDelegate (OFMemoryStreamTests) -- (void)memoryStreamTests -{ - void *pool = objc_autoreleasePoolPush(); - OFMemoryStream *stream; - char buffer[10]; - OFMutableData *data; - - TEST(@"+[streamWithMemoryAddress:size:writable:]", - (stream = [OFMemoryStream streamWithMemoryAddress: (char *)string - size: sizeof(string) - writable: false])); +@implementation OFMemoryStreamTests +- (void)testReadOnlyMemoryStream +{ + OFMemoryStream *stream = [OFMemoryStream + streamWithMemoryAddress: (char *)string + size: sizeof(string) + writable: false]; + char buffer[10]; /* * Test the lowlevel methods, as otherwise OFStream will do one big * read and we will not test OFMemoryStream. */ - TEST(@"-[lowlevelReadIntoBuffer:length:]", - [stream lowlevelReadIntoBuffer: buffer length: 5] == 5 && - memcmp(buffer, "abcde", 5) == 0 && - [stream lowlevelReadIntoBuffer: buffer length: 3] == 3 && - memcmp(buffer, "fgh", 3) == 0 && - [stream lowlevelReadIntoBuffer: buffer length: 10] == 5 && - memcmp(buffer, "ijkl", 5) == 0) - - TEST(@"-[lowlevelIsAtEndOfStream]", [stream lowlevelIsAtEndOfStream]) - - TEST(@"-[lowlevelSeekToOffset:whence:]", - [stream lowlevelSeekToOffset: 0 whence: OFSeekCurrent] == - sizeof(string) && [stream lowlevelIsAtEndOfStream] && - [stream lowlevelSeekToOffset: 4 whence: OFSeekSet] == 4 && - ![stream lowlevelIsAtEndOfStream] && - [stream lowlevelReadIntoBuffer: buffer length: 10] == 9 && - memcmp(buffer, "efghijkl", 9) == 0 && - [stream lowlevelSeekToOffset: -2 whence: OFSeekEnd] == 11 && - [stream lowlevelReadIntoBuffer: buffer length: 10] == 2 && - memcmp(buffer, "l", 2) == 0 && - [stream lowlevelReadIntoBuffer: buffer length: 10] == 0) - - EXPECT_EXCEPTION(@"Writes rejected on read-only stream", - OFWriteFailedException, [stream lowlevelWriteBuffer: "" length: 1]) - - data = [OFMutableData dataWithCapacity: 13]; + OTAssertEqual([stream lowlevelReadIntoBuffer: buffer length: 5], 5); + OTAssertEqual(memcmp(buffer, "abcde", 5), 0); + OTAssertEqual([stream lowlevelReadIntoBuffer: buffer length: 3], 3); + OTAssertEqual(memcmp(buffer, "fgh", 3), 0); + OTAssertEqual([stream lowlevelReadIntoBuffer: buffer length: 10], 5); + OTAssertEqual(memcmp(buffer, "ijkl", 5), 0); + OTAssertTrue([stream lowlevelIsAtEndOfStream]); + + OTAssertEqual([stream lowlevelSeekToOffset: 0 whence: OFSeekCurrent], + sizeof(string)); + OTAssertTrue([stream lowlevelIsAtEndOfStream]); + + OTAssertEqual([stream lowlevelSeekToOffset: 4 whence: OFSeekSet], 4); + OTAssertFalse([stream lowlevelIsAtEndOfStream]); + OTAssertEqual([stream lowlevelReadIntoBuffer: buffer length: 10], 9); + OTAssertEqual(memcmp(buffer, "efghijkl", 9), 0); + + OTAssertEqual([stream lowlevelSeekToOffset: -2 whence: OFSeekEnd], 11); + OTAssertEqual([stream lowlevelReadIntoBuffer: buffer length: 10], 2); + OTAssertEqual(memcmp(buffer, "l", 2), 0); + OTAssertEqual([stream lowlevelReadIntoBuffer: buffer length: 10], 0); + + OTAssertThrowsSpecific([stream lowlevelWriteBuffer: "" length: 1], + OFWriteFailedException); +} + +- (void)testReadWriteMemoryStream +{ + OFMutableData *data = [OFMutableData dataWithCapacity: 13]; + OFMemoryStream *stream; + [data increaseCountBy: 13]; stream = [OFMemoryStream streamWithMemoryAddress: data.mutableItems size: data.count writable: true]; - TEST(@"-[lowlevelWriteBuffer:length:]", - [stream lowlevelWriteBuffer: "abcde" length: 5] == 5 && - [stream lowlevelWriteBuffer: "fgh" length: 3] == 3 && - [stream lowlevelWriteBuffer: "ijkl" length: 5] == 5 && - memcmp(data.items, string, data.count) == 0 && - [stream lowlevelSeekToOffset: -3 whence: OFSeekEnd] == 10) - - EXPECT_EXCEPTION(@"Out of bound writes rejected", - OFWriteFailedException, - [stream lowlevelWriteBuffer: "xyz" length: 4]) - - TEST(@"Partial write for too long write", - memcmp(data.items, "abcdefghijxyz", 13) == 0) - - objc_autoreleasePoolPop(pool); + + OTAssertEqual([stream lowlevelWriteBuffer: "abcde" length: 5], 5); + OTAssertEqual([stream lowlevelWriteBuffer: "fgh" length: 3], 3); + OTAssertEqual([stream lowlevelWriteBuffer: "ijkl" length: 5], 5); + OTAssertEqual(memcmp(data.items, string, data.count), 0); + OTAssertEqual([stream lowlevelSeekToOffset: -3 whence: OFSeekEnd], 10); + + OTAssertThrowsSpecific([stream lowlevelWriteBuffer: "xyz" length: 4], + OFWriteFailedException); } @end Index: tests/OFMethodSignatureTests.m ================================================================== --- tests/OFMethodSignatureTests.m +++ tests/OFMethodSignatureTests.m @@ -19,13 +19,15 @@ #if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H) # include #endif -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFMethodSignature"; +@interface OFMethodSignatureTests: OTTestCase +@end struct Test1Struct { char c; int i; char d; @@ -67,115 +69,106 @@ float f; double d; } u; }; -@implementation TestsAppDelegate (OFMethodSignatureTests) -- (void)methodSignatureTests -{ - void *pool = objc_autoreleasePoolPush(); - OFMethodSignature *methodSignature; - - TEST(@"-[signatureWithObjCTypes:] #1", - (methodSignature = [OFMethodSignature signatureWithObjCTypes: - "i28@0:8S16*20"]) && - methodSignature.numberOfArguments == 4 && - strcmp(methodSignature.methodReturnType, "i") == 0 && - strcmp([methodSignature argumentTypeAtIndex: 0], "@") == 0 && - strcmp([methodSignature argumentTypeAtIndex: 1], ":") == 0 && - strcmp([methodSignature argumentTypeAtIndex: 2], "S") == 0 && - strcmp([methodSignature argumentTypeAtIndex: 3], "*") == 0 && - methodSignature.frameLength == 28 && - [methodSignature argumentOffsetAtIndex: 0] == 0 && - [methodSignature argumentOffsetAtIndex: 1] == 8 && - [methodSignature argumentOffsetAtIndex: 2] == 16 && - [methodSignature argumentOffsetAtIndex: 3] == 20) - - TEST(@"-[signatureWithObjCTypes:] #2", - (methodSignature = [OFMethodSignature signatureWithObjCTypes: - "{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}24@0:8" - "^{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}16"]) && - methodSignature.numberOfArguments == 3 && - strcmp(methodSignature.methodReturnType, - "{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}") == 0 && - strcmp([methodSignature argumentTypeAtIndex: 0], "@") == 0 && - strcmp([methodSignature argumentTypeAtIndex: 1], ":") == 0 && - strcmp([methodSignature argumentTypeAtIndex: 2], - "^{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}") == 0 && - methodSignature.frameLength == 24 && - [methodSignature argumentOffsetAtIndex: 0] == 0 && - [methodSignature argumentOffsetAtIndex: 1] == 8 && - [methodSignature argumentOffsetAtIndex: 2] == 16) - - EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #3", - OFInvalidFormatException, - [OFMethodSignature signatureWithObjCTypes: "{ii"]) - - EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #4", - OFInvalidFormatException, - [OFMethodSignature signatureWithObjCTypes: ""]) - - EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #5", - OFInvalidFormatException, - [OFMethodSignature signatureWithObjCTypes: "0"]) - - EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #6", - OFInvalidFormatException, - [OFMethodSignature signatureWithObjCTypes: "{{}0"]) - - TEST(@"OFSizeOfTypeEncoding() #1", - OFSizeOfTypeEncoding(@encode(struct Test1Struct)) == - sizeof(struct Test1Struct)) - - TEST(@"OFSizeOfTypeEncoding() #2", - OFSizeOfTypeEncoding(@encode(struct Test2Struct)) == - sizeof(struct Test2Struct)) - -#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H) && \ - OF_GCC_VERSION >= 402 - TEST(@"OFSizeOfTypeEncoding() #3", - OFSizeOfTypeEncoding(@encode(struct Test3Struct)) == - sizeof(struct Test3Struct)) -#endif - - TEST(@"OFSizeOfTypeEncoding() #4", - OFSizeOfTypeEncoding(@encode(union Test3Union)) == - sizeof(union Test3Union)) - - TEST(@"OFSizeOfTypeEncoding() #5", - OFSizeOfTypeEncoding(@encode(union Test4Union)) == - sizeof(union Test4Union)) - - TEST(@"OFSizeOfTypeEncoding() #6", - OFSizeOfTypeEncoding(@encode(struct Test1Struct [5])) == - sizeof(struct Test1Struct [5])) - - TEST(@"OFAlignmentOfTypeEncoding() #1", - OFAlignmentOfTypeEncoding(@encode(struct Test1Struct)) == - OF_ALIGNOF(struct Test1Struct)) - - TEST(@"OFAlignmentOfTypeEncoding() #2", - OFAlignmentOfTypeEncoding(@encode(struct Test2Struct)) == - OF_ALIGNOF(struct Test2Struct)) - -#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H) && \ - OF_GCC_VERSION >= 402 - TEST(@"OFAlignmentOfTypeEncoding() #3", - OFAlignmentOfTypeEncoding(@encode(struct Test3Struct)) == - OF_ALIGNOF(struct Test3Struct)) -#endif - - TEST(@"OFAlignmentOfTypeEncoding() #4", - OFAlignmentOfTypeEncoding(@encode(union Test3Union)) == - OF_ALIGNOF(union Test3Union)) - - TEST(@"OFAlignmentOfTypeEncoding() #5", - OFAlignmentOfTypeEncoding(@encode(union Test4Union)) == - OF_ALIGNOF(union Test4Union)) - - TEST(@"OFAlignmentOfTypeEncoding() #6", - OFAlignmentOfTypeEncoding(@encode(struct Test1Struct [5])) == - OF_ALIGNOF(struct Test1Struct [5])) - - objc_autoreleasePoolPop(pool); +@implementation OFMethodSignatureTests +- (void)testSignatureWithObjCTypes +{ + OFMethodSignature *methodSignature; + + methodSignature = + [OFMethodSignature signatureWithObjCTypes: "i28@0:8S16*20"]; + OTAssertEqual(methodSignature.numberOfArguments, 4); + OTAssertEqual(strcmp(methodSignature.methodReturnType, "i"), 0); + OTAssertEqual(strcmp([methodSignature argumentTypeAtIndex: 0], "@"), 0); + OTAssertEqual(strcmp([methodSignature argumentTypeAtIndex: 1], ":"), 0); + OTAssertEqual(strcmp([methodSignature argumentTypeAtIndex: 2], "S"), 0); + OTAssertEqual(strcmp([methodSignature argumentTypeAtIndex: 3], "*"), 0); + OTAssertEqual(methodSignature.frameLength, 28); + OTAssertEqual([methodSignature argumentOffsetAtIndex: 0], 0); + OTAssertEqual([methodSignature argumentOffsetAtIndex: 1], 8); + OTAssertEqual([methodSignature argumentOffsetAtIndex: 2], 16); + OTAssertEqual([methodSignature argumentOffsetAtIndex: 3], 20); + + methodSignature = [OFMethodSignature signatureWithObjCTypes: + "{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}24@0:8" + "^{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}16"]; + OTAssertEqual(methodSignature.numberOfArguments, 3); + OTAssertEqual(strcmp(methodSignature.methodReturnType, + "{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}"), 0); + OTAssertEqual(strcmp([methodSignature argumentTypeAtIndex: 0], "@"), 0); + OTAssertEqual(strcmp([methodSignature argumentTypeAtIndex: 1], ":"), 0); + OTAssertEqual(strcmp([methodSignature argumentTypeAtIndex: 2], + "^{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}"), 0); + OTAssertEqual(methodSignature.frameLength, 24); + OTAssertEqual([methodSignature argumentOffsetAtIndex: 0], 0); + OTAssertEqual([methodSignature argumentOffsetAtIndex: 1], 8); + OTAssertEqual([methodSignature argumentOffsetAtIndex: 2], 16); +} + +- (void)testSignatureWithObjCTypesFailsWithInvalidFormat +{ + OTAssertThrowsSpecific( + [OFMethodSignature signatureWithObjCTypes: "{ii"], + OFInvalidFormatException); + + OTAssertThrowsSpecific([OFMethodSignature signatureWithObjCTypes: ""], + OFInvalidFormatException); + + OTAssertThrowsSpecific([OFMethodSignature signatureWithObjCTypes: "0"], + OFInvalidFormatException); + + OTAssertThrowsSpecific( + [OFMethodSignature signatureWithObjCTypes: "{{}0"], + OFInvalidFormatException); +} + +- (void)testSizeOfTypeEncoding +{ + OTAssertEqual(OFSizeOfTypeEncoding(@encode(struct Test1Struct)), + sizeof(struct Test1Struct)); + + OTAssertEqual(OFSizeOfTypeEncoding(@encode(struct Test2Struct)), + sizeof(struct Test2Struct)); + +#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H) && \ + OF_GCC_VERSION >= 402 + OTAssertEqual(OFSizeOfTypeEncoding(@encode(struct Test3Struct)), + sizeof(struct Test3Struct)); +#endif + + OTAssertEqual(OFSizeOfTypeEncoding(@encode(union Test3Union)), + sizeof(union Test3Union)); + + OTAssertEqual(OFSizeOfTypeEncoding(@encode(union Test4Union)), + sizeof(union Test4Union)); + + OTAssertEqual(OFSizeOfTypeEncoding(@encode(struct Test1Struct [5])), + sizeof(struct Test1Struct [5])); +} + +- (void)testAlignmentOfTypeEncoding +{ + OTAssertEqual(OFAlignmentOfTypeEncoding(@encode(struct Test1Struct)), + OF_ALIGNOF(struct Test1Struct)); + + OTAssertEqual(OFAlignmentOfTypeEncoding(@encode(struct Test2Struct)), + OF_ALIGNOF(struct Test2Struct)); + +#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H) && \ + OF_GCC_VERSION >= 402 + OTAssertEqual(OFAlignmentOfTypeEncoding(@encode(struct Test3Struct)), + OF_ALIGNOF(struct Test3Struct)); +#endif + + OTAssertEqual(OFAlignmentOfTypeEncoding(@encode(union Test3Union)), + OF_ALIGNOF(union Test3Union)); + + OTAssertEqual(OFAlignmentOfTypeEncoding(@encode(union Test4Union)), + OF_ALIGNOF(union Test4Union)); + + OTAssertEqual( + OFAlignmentOfTypeEncoding(@encode(struct Test1Struct [5])), + OF_ALIGNOF(struct Test1Struct [5])); } @end ADDED tests/OFMutableArrayTests.h Index: tests/OFMutableArrayTests.h ================================================================== --- /dev/null +++ tests/OFMutableArrayTests.h @@ -0,0 +1,22 @@ +/* + * 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 "OFArrayTests.h" + +@interface OFMutableArrayTests: OFArrayTests +{ + OFMutableArray *_mutableArray; +} +@end ADDED tests/OFMutableArrayTests.m Index: tests/OFMutableArrayTests.m ================================================================== --- /dev/null +++ tests/OFMutableArrayTests.m @@ -0,0 +1,223 @@ +/* + * 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 "OFMutableArrayTests.h" + +#import "OFArray+Private.h" + +@interface CustomMutableArray: OFMutableArray +{ + OFMutableArray *_array; +} +@end + +static OFString *const cArray[] = { + @"Foo", + @"Bar", + @"Baz" +}; + +@implementation OFMutableArrayTests +- (Class)arrayClass +{ + return [CustomMutableArray class]; +} + +- (void)setUp +{ + [super setUp]; + + _mutableArray = [[self.arrayClass alloc] + initWithObjects: cArray + count: sizeof(cArray) / sizeof(*cArray)]; +} + +- (void)dealloc +{ + [_mutableArray release]; + + [super dealloc]; +} + +- (void)testAddObject +{ + [_mutableArray addObject: cArray[0]]; + [_mutableArray addObject: cArray[2]]; + + OTAssertEqualObjects(_mutableArray, + ([OFArray arrayWithObjects: @"Foo", @"Bar", @"Baz", @"Foo", @"Baz", + nil])); +} + +- (void)testInsertObjectAtIndex +{ + [_mutableArray insertObject: cArray[1] atIndex: 1]; + + OTAssertEqualObjects(_mutableArray, + ([OFArray arrayWithObjects: @"Foo", @"Bar", @"Bar", @"Baz", nil])); +} + +- (void)testReplaceObjectWithObject +{ + [_mutableArray insertObject: cArray[1] atIndex: 1]; + [_mutableArray replaceObject: cArray[1] withObject: cArray[0]]; + + OTAssertEqualObjects(_mutableArray, + ([OFArray arrayWithObjects: @"Foo", @"Foo", @"Foo", @"Baz", nil])); +} + +- (void)testReplaceObjectIdenticalToWithObject +{ + [_mutableArray insertObject: [[cArray[1] mutableCopy] autorelease] + atIndex: 1]; + [_mutableArray replaceObjectIdenticalTo: cArray[1] + withObject: cArray[0]]; + + OTAssertEqualObjects(_mutableArray, + ([OFArray arrayWithObjects: @"Foo", @"Bar", @"Foo", @"Baz", nil])); +} + +- (void)testReplaceObjectAtIndexWithObject +{ + [_mutableArray replaceObjectAtIndex: 1 + withObject: cArray[0]]; + + OTAssertEqualObjects(_mutableArray, + ([OFArray arrayWithObjects: @"Foo", @"Foo", @"Baz", nil])); +} + +- (void)testRemoveObject +{ + [_mutableArray removeObject: cArray[1]]; + + OTAssertEqualObjects(_mutableArray, + ([OFArray arrayWithObjects: @"Foo", @"Baz", nil])); +} + +- (void)testRemoveObjectIdenticalTo +{ + [_mutableArray removeObjectIdenticalTo: cArray[1]]; + + OTAssertEqualObjects(_mutableArray, + ([OFArray arrayWithObjects: @"Foo", @"Baz", nil])); +} + +- (void)testRemoveObjectAtIndex +{ + [_mutableArray removeObjectAtIndex: 1]; + + OTAssertEqualObjects(_mutableArray, + ([OFArray arrayWithObjects: @"Foo", @"Baz", nil])); +} + +- (void)testRemoveObjectsInRange +{ + [_mutableArray removeObjectsInRange: OFMakeRange(1, 2)]; + + OTAssertEqualObjects(_mutableArray, [OFArray arrayWithObject: @"Foo"]); +} + +- (void)testRemoveObjectsInRangeFailsWhenOutOfRange +{ + OTAssertThrowsSpecific([_mutableArray removeObjectsInRange: + OFMakeRange(0, _mutableArray.count + 1)], OFOutOfRangeException); +} + +- (void)testReverse +{ + [_mutableArray reverse]; + + OTAssertEqualObjects(_mutableArray, + ([OFArray arrayWithObjects: @"Baz", @"Bar", @"Foo", nil])); +} + +#ifdef OF_HAVE_BLOCKS +- (void)testReplaceObjectsUsingBlock +{ + [_mutableArray replaceObjectsUsingBlock: ^ id (id object, size_t idx) { + return [object lowercaseString]; + }]; + + OTAssertEqualObjects(_mutableArray, + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])); +} +#endif + +- (void)testSetValueForKey +{ + OFMutableArray *array = [self.arrayClass arrayWithObjects: + [OFMutableIRI IRIWithString: @"http://foo.bar/"], + [OFMutableIRI IRIWithString: @"http://bar.qux/"], + [OFMutableIRI IRIWithString: @"http://qux.quxqux/"], nil]; + + [array setValue: [OFNumber numberWithShort: 1234] + forKey: @"port"]; + OTAssertEqualObjects(array, ([OFArray arrayWithObjects: + [OFIRI IRIWithString: @"http://foo.bar:1234/"], + [OFIRI IRIWithString: @"http://bar.qux:1234/"], + [OFIRI IRIWithString: @"http://qux.quxqux:1234/"], nil])); +} +@end + +@implementation CustomMutableArray +- (instancetype)initWithObjects: (id const *)objects count: (size_t)count +{ + self = [super init]; + + @try { + _array = [[OFMutableArray alloc] initWithObjects: objects + count: count]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_array release]; + + [super dealloc]; +} + +- (id)objectAtIndex: (size_t)idx +{ + return [_array objectAtIndex: idx]; +} + +- (size_t)count +{ + return [_array count]; +} + +- (void)insertObject: (id)object atIndex: (size_t)idx +{ + [_array insertObject: object atIndex: idx]; +} + +- (void)replaceObjectAtIndex: (size_t)idx withObject: (id)object +{ + [_array replaceObjectAtIndex: idx withObject: object]; +} + +- (void)removeObjectAtIndex: (size_t)idx +{ + [_array removeObjectAtIndex: idx]; +} +@end ADDED tests/OFMutableDataTests.m Index: tests/OFMutableDataTests.m ================================================================== --- /dev/null +++ tests/OFMutableDataTests.m @@ -0,0 +1,117 @@ +/* + * 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" + +#include + +#import "OFDataTests.h" + +@interface OFMutableDataTests: OFDataTests +{ + OFMutableData *_mutableData; +} +@end + +@implementation OFMutableDataTests +- (Class)dataClass +{ + return [OFMutableData class]; +} + +- (void)setUp +{ + [super setUp]; + + _mutableData = [[OFMutableData alloc] initWithItems: "abcdef" count: 6]; +} + +- (void)dealloc +{ + [_mutableData release]; + + [super dealloc]; +} + +- (void)testMutableCopy +{ + OTAssertEqualObjects([[_data mutableCopy] autorelease], _data); + OTAssertNotEqual([[_data mutableCopy] autorelease], _data); +} + +- (void)testAddItem +{ + [_mutableData addItem: "g"]; + + OTAssertEqualObjects(_mutableData, + [OFData dataWithItems: "abcdefg" count: 7]); +} + +- (void)testAddItemsCount +{ + [_mutableData addItems: "gh" count: 2]; + + OTAssertEqualObjects(_mutableData, + [OFData dataWithItems: "abcdefgh" count: 8]); +} + +- (void)testAddItemsCountThrowsOnOutOfRange +{ + OTAssertThrowsSpecific([_mutableData addItems: "" count: SIZE_MAX], + OFOutOfRangeException); +} + +- (void)testRemoveLastItem +{ + [_mutableData removeLastItem]; + + OTAssertEqualObjects(_mutableData, + [OFData dataWithItems: "abcde" count: 5]); +} + +- (void)testRemoveItemsInRange +{ + [_mutableData removeItemsInRange: OFMakeRange(1, 2)]; + + OTAssertEqualObjects(_mutableData, + [OFData dataWithItems: "adef" count: 4]); +} + +- (void)testRemoveItemsInRangeThrowsOnOutOfRangeRange +{ + OTAssertThrowsSpecific( + [_mutableData removeItemsInRange: OFMakeRange(6, 1)], + OFOutOfRangeException); + + OTAssertThrowsSpecific( + [_mutableData removeItemsInRange: OFMakeRange(7, 0)], + OFOutOfRangeException); +} + +- (void)testInsertItemsAtIndexCount +{ + [_mutableData insertItems: "BC" atIndex: 1 count: 2]; + + OTAssertEqualObjects(_mutableData, + [OFData dataWithItems: "aBCbcdef" count: 8]); +} + +- (void)testInsertItemsAtIndexCountThrowsOnOutOfRangeIndex +{ + OTAssertThrowsSpecific( + [_mutableData insertItems: "a" atIndex: 7 count: 1], + OFOutOfRangeException); +} +@end ADDED tests/OFMutableDictionaryTests.h Index: tests/OFMutableDictionaryTests.h ================================================================== --- /dev/null +++ tests/OFMutableDictionaryTests.h @@ -0,0 +1,25 @@ +/* + * 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 "ObjFW.h" +#import "ObjFWTest.h" + +#import "OFDictionaryTests.h" + +@interface OFMutableDictionaryTests: OFDictionaryTests +{ + OFMutableDictionary *_mutableDictionary; +} +@end ADDED tests/OFMutableDictionaryTests.m Index: tests/OFMutableDictionaryTests.m ================================================================== --- /dev/null +++ tests/OFMutableDictionaryTests.m @@ -0,0 +1,174 @@ +/* + * 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 "OFMutableDictionaryTests.h" + +@interface CustomMutableDictionary: OFMutableDictionary +{ + OFMutableDictionary *_dictionary; +} +@end + +@implementation OFMutableDictionaryTests +- (Class)dictionaryClass +{ + return [CustomMutableDictionary class]; +} + +- (void)setUp +{ + [super setUp]; + + _mutableDictionary = [[self.dictionaryClass alloc] init]; +} + +- (void)dealloc +{ + [_mutableDictionary release]; + + [super dealloc]; +} + +- (void)testSetObjectForKey +{ + [_mutableDictionary setObject: @"bar" forKey: @"foo"]; + OTAssertEqualObjects([_mutableDictionary objectForKey: @"foo"], @"bar"); + + [_mutableDictionary setObject: @"qux" forKey: @"baz"]; + OTAssertEqualObjects(_mutableDictionary, + ([OFDictionary dictionaryWithKeysAndObjects: + @"foo", @"bar", @"baz", @"qux", nil])); +} + +- (void)testSetValueForKey +{ + [_mutableDictionary setValue: @"bar" forKey: @"foo"]; + OTAssertEqualObjects([_mutableDictionary objectForKey: @"foo"], @"bar"); + + [_mutableDictionary setValue: @"qux" forKey: @"baz"]; + OTAssertEqualObjects(_mutableDictionary, + ([OFDictionary dictionaryWithKeysAndObjects: + @"foo", @"bar", @"baz", @"qux", nil])); +} + +- (void)testRemoveObjectForKey +{ + [_mutableDictionary addEntriesFromDictionary: _dictionary]; + OTAssertEqual(_mutableDictionary.count, 2); + + [_mutableDictionary removeObjectForKey: @"key2"]; + OTAssertEqual(_mutableDictionary.count, 1); + OTAssertEqualObjects(_mutableDictionary, + [OFDictionary dictionaryWithObject: @"value1" forKey: @"key1"]); +} + +- (void)testMutableCopy +{ + OFMutableDictionary *copy = [[_dictionary mutableCopy] autorelease]; + + OTAssertEqualObjects(copy, _dictionary); + OTAssertNotEqual(copy, _dictionary); +} + +#ifdef OF_HAVE_BLOCKS +- (void)testReplaceObjectsUsingBlock +{ + OFMutableDictionary *mutableDictionary = + [[_dictionary mutableCopy] autorelease]; + + [mutableDictionary replaceObjectsUsingBlock: ^ id (id key, id object) { + if ([key isEqual: @"key1"]) + return @"value_1"; + if ([key isEqual: @"key2"]) + return @"value_2"; + + return nil; + }]; + + OTAssertEqualObjects(mutableDictionary, + ([OFDictionary dictionaryWithKeysAndObjects: + @"key1", @"value_1", @"key2", @"value_2", nil])); +} +#endif +@end + +@implementation CustomMutableDictionary +- (instancetype)init +{ + self = [super init]; + + @try { + _dictionary = [[OFMutableDictionary alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)initWithObjects: (const id *)objects_ + forKeys: (const id *)keys_ + count: (size_t)count +{ + self = [super init]; + + @try { + _dictionary = [[OFMutableDictionary alloc] + initWithObjects: objects_ + forKeys: keys_ + count: count]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_dictionary release]; + + [super dealloc]; +} + +- (id)objectForKey: (id)key +{ + return [_dictionary objectForKey: key]; +} + +- (size_t)count +{ + return _dictionary.count; +} + +- (OFEnumerator *)keyEnumerator +{ + return [_dictionary keyEnumerator]; +} + +- (void)setObject: (id)object forKey: (id)key +{ + [_dictionary setObject: object forKey: key]; +} + +- (void)removeObjectForKey: (id)key +{ + [_dictionary removeObjectForKey: key]; +} +@end ADDED tests/OFMutableSetTests.h Index: tests/OFMutableSetTests.h ================================================================== --- /dev/null +++ tests/OFMutableSetTests.h @@ -0,0 +1,22 @@ +/* + * 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 "OFSetTests.h" + +@interface OFMutableSetTests: OFSetTests +{ + OFMutableSet *_mutableSet; +} +@end ADDED tests/OFMutableSetTests.m Index: tests/OFMutableSetTests.m ================================================================== --- /dev/null +++ tests/OFMutableSetTests.m @@ -0,0 +1,142 @@ +/* + * 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 "OFMutableSetTests.h" + +@interface CustomMutableSet: OFMutableSet +{ + OFMutableSet *_set; +} +@end + +@implementation OFMutableSetTests +- (Class)setClass +{ + return [CustomMutableSet class]; +} + +- (void)setUp +{ + [super setUp]; + + _mutableSet = [[OFMutableSet alloc] + initWithObjects: @"foo", @"bar", @"baz", nil]; +} + +- (void)dealloc +{ + [_mutableSet release]; + + [super dealloc]; +} + +- (void)testAddObject +{ + [_mutableSet addObject: @"x"]; + + OTAssertEqualObjects(_mutableSet, + ([OFSet setWithObjects: @"foo", @"bar", @"baz", @"x", nil])); +} + +- (void)testRemoveObject +{ + [_mutableSet removeObject: @"foo"]; + + OTAssertEqualObjects(_mutableSet, + ([OFSet setWithObjects: @"bar", @"baz", nil])); +} + +- (void)testMinusSet +{ + [_mutableSet minusSet: [OFSet setWithObjects: @"foo", @"bar", nil]]; + + OTAssertEqualObjects(_mutableSet, + ([OFSet setWithObjects: @"baz", nil])); +} + +- (void)testIntersectSet +{ + [_mutableSet intersectSet: [OFSet setWithObjects: @"foo", @"qux", nil]]; + + OTAssertEqualObjects(_mutableSet, + ([OFSet setWithObjects: @"foo", nil])); +} + +- (void)testUnionSet +{ + [_mutableSet unionSet: [OFSet setWithObjects: @"x", @"y", nil]]; + + OTAssertEqualObjects(_mutableSet, + ([OFSet setWithObjects: @"foo", @"bar", @"baz", @"x", @"y", nil])); +} + +- (void)testRemoveAllObjects +{ + [_mutableSet removeAllObjects]; + + OTAssertEqual(_mutableSet.count, 0); +} +@end + +@implementation CustomMutableSet +- (instancetype)initWithObjects: (id const *)objects count: (size_t)count +{ + self = [super init]; + + @try { + _set = [[OFMutableSet alloc] initWithObjects: objects + count: count]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_set release]; + + [super dealloc]; +} + +- (size_t)count +{ + return _set.count; +} + +- (bool)containsObject: (id)object +{ + return [_set containsObject: object]; +} + +- (OFEnumerator *)objectEnumerator +{ + return [_set objectEnumerator]; +} + +- (void)addObject: (id)object +{ + [_set addObject: object]; +} + +- (void)removeObject: (id)object +{ + [_set removeObject: object]; +} +@end ADDED tests/OFMutableStringTests.h Index: tests/OFMutableStringTests.h ================================================================== --- /dev/null +++ tests/OFMutableStringTests.h @@ -0,0 +1,25 @@ +/* + * 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 "ObjFW.h" +#import "ObjFWTest.h" + +#import "OFStringTests.h" + +@interface OFMutableStringTests: OFStringTests +{ + OFMutableString *_mutableString; +} +@end ADDED tests/OFMutableStringTests.m Index: tests/OFMutableStringTests.m ================================================================== --- /dev/null +++ tests/OFMutableStringTests.m @@ -0,0 +1,408 @@ +/* + * 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" + +#include + +#import "OFMutableStringTests.h" + +@interface CustomMutableString: OFMutableString +{ + OFMutableString *_string; +} +@end + +static OFString *const whitespace[] = { + @" \r \t\n\t \tasd \t \t\t\r\n", + @" \t\t \t\t \t \t" +}; + +@implementation OFMutableStringTests +- (Class)stringClass +{ + return [CustomMutableString class]; +} + +- (void)setUp +{ + [super setUp]; + + _mutableString = [[self.stringClass alloc] initWithString: @"täṠ€🤔"]; +} + +- (void)dealloc +{ + [_mutableString release]; + + [super dealloc]; +} + +- (void)testAppendString +{ + [_mutableString appendString: @"ö"]; + + OTAssertEqualObjects(_mutableString, @"täṠ€🤔ö"); +} + +- (void)testAppendUTF8String +{ + [_mutableString appendUTF8String: "ö"]; + + OTAssertEqualObjects(_mutableString, @"täṠ€🤔ö"); +} + +- (void)testAppendUTF8StringLength +{ + [_mutableString appendUTF8String: "\xEF\xBB\xBF" "öÖ" length: 7]; + + OTAssertEqualObjects(_mutableString, @"täṠ€🤔öÖ"); +} + +- (void)testAppendFormat +{ + [_mutableString appendFormat: @"%02X", 15]; + + OTAssertEqualObjects(_mutableString, @"täṠ€🤔0F"); +} + +- (void)testAppendCharactersLength +{ + [_mutableString appendCharacters: (OFUnichar []){ 0xF6, 0xD6 } + length: 2]; + + OTAssertEqualObjects(_mutableString, @"täṠ€🤔öÖ"); +} + +- (void)testUppercase +{ + [_mutableString uppercase]; + +#ifdef OF_HAVE_UNICODE_TABLES + OTAssertEqualObjects(_mutableString, @"TÄṠ€🤔"); +#else + OTAssertEqualObjects(_mutableString, @"TäṠ€🤔"); +#endif +} + +- (void)testLowercase +{ + [_mutableString lowercase]; + +#ifdef OF_HAVE_UNICODE_TABLES + OTAssertEqualObjects(_mutableString, @"täṡ€🤔"); +#else + OTAssertEqualObjects(_mutableString, @"täṠ€🤔"); +#endif +} + +- (void)testCapitalize +{ + OFMutableString *string = + [self.stringClass stringWithString: @"täṠ€🤔täṠ€🤔 täṠ€🤔"]; + + [string capitalize]; + +#ifdef OF_HAVE_UNICODE_TABLES + OTAssertEqualObjects(string, @"Täṡ€🤔täṡ€🤔 Täṡ€🤔"); +#else + OTAssertEqualObjects(string, @"TäṠ€🤔täṠ€🤔 TäṠ€🤔"); +#endif +} + +- (void)testInsertStringAtIndex +{ + [_mutableString insertString: @"fööbär" atIndex: 2]; + + OTAssertEqualObjects(_mutableString, @"täfööbärṠ€🤔"); +} + +- (void)testSetCharacterAtIndex +{ + [_mutableString setCharacter: 0x1F600 atIndex: 2]; + + OTAssertEqualObjects(_mutableString, @"tä😀€🤔"); +} + +- (void)testDeleteCharactersInRange +{ + [_mutableString deleteCharactersInRange: OFMakeRange(2, 2)]; + + OTAssertEqualObjects(_mutableString, @"tä🤔"); +} + +- (void)testDeleteCharactersInRangeThrowsWithOutOfRangeRange +{ + OTAssertThrowsSpecific( + [_mutableString deleteCharactersInRange: OFMakeRange(4, 2)], + OFOutOfRangeException); + + OTAssertThrowsSpecific( + [_mutableString deleteCharactersInRange: OFMakeRange(5, 1)], + OFOutOfRangeException); + + OTAssertThrowsSpecific( + [_mutableString deleteCharactersInRange: OFMakeRange(6, 0)], + OFOutOfRangeException); +} + +- (void)testReplaceCharactersInRangeWithString +{ + OFMutableString *string = + [self.stringClass stringWithString: @"𝄞öööbä€"]; + + [string replaceCharactersInRange: OFMakeRange(1, 3) + withString: @"äöüß"]; + OTAssertEqualObjects(string, @"𝄞äöüßbä€"); + + [string replaceCharactersInRange: OFMakeRange(4, 2) withString: @"b"]; + OTAssertEqualObjects(string, @"𝄞äöübä€"); + + [string replaceCharactersInRange: OFMakeRange(0, 7) withString: @""]; + OTAssertEqualObjects(string, @""); +} + +- (void)testReplaceCharactersInRangeWithStringFailsWithOutOfRangeRange +{ + OTAssertThrowsSpecific( + [_mutableString replaceCharactersInRange: OFMakeRange(4, 2) + withString: @"abc"], + OFOutOfRangeException); + + OTAssertThrowsSpecific( + [_mutableString replaceCharactersInRange: OFMakeRange(5, 1) + withString: @"abc"], + OFOutOfRangeException); + + OTAssertThrowsSpecific( + [_mutableString replaceCharactersInRange: OFMakeRange(6, 0) + withString: @""], + OFOutOfRangeException); +} + +- (void)testReplaceOccurrencesOfStringWithString +{ + OFMutableString *string; + + string = [self.stringClass stringWithString: @"asd fo asd fofo asd"]; + [string replaceOccurrencesOfString: @"fo" withString: @"foo"]; + OTAssertEqualObjects(string, @"asd foo asd foofoo asd"); + + string = [self.stringClass stringWithString: @"XX"]; + [string replaceOccurrencesOfString: @"X" withString: @"XX"]; + OTAssertEqualObjects(string, @"XXXX"); +} + +- (void)testReplaceOccurrencesOfStringWithStringOptionsRange +{ + OFMutableString *string = + [self.stringClass stringWithString: @"foofoobarfoobarfoo"]; + + [string replaceOccurrencesOfString: @"oo" + withString: @"óò" + options: 0 + range: OFMakeRange(2, 15)]; + OTAssertEqualObjects(string, @"foofóòbarfóòbarfoo"); +} + +- (void) + testReplaceOccurrencesOfStringWithStringOptionsRangeThrowsWithOutOfRangeRange +{ + OTAssertThrowsSpecific( + [_mutableString replaceOccurrencesOfString: @"t" + withString: @"abc" + options: 0 + range: OFMakeRange(4, 2)], + OFOutOfRangeException); + + OTAssertThrowsSpecific( + [_mutableString replaceOccurrencesOfString: @"t" + withString: @"abc" + options: 0 + range: OFMakeRange(5, 1)], + OFOutOfRangeException); + + OTAssertThrowsSpecific( + [_mutableString replaceOccurrencesOfString: @"t" + withString: @"" + options: 0 + range: OFMakeRange(6, 0)], + OFOutOfRangeException); +} + +- (void)deleteLeadingWhitespaces +{ + OFMutableString *string; + + string = [self.stringClass stringWithString: whitespace[0]]; + [string deleteLeadingWhitespaces]; + OTAssertEqualObjects(string, @"asd \t \t\t\r\n"); + + string = [self.stringClass stringWithString: whitespace[1]]; + [string deleteLeadingWhitespaces]; + OTAssertEqualObjects(string, @""); +} + +- (void)deleteTrailingWhitespaces +{ + OFMutableString *string; + + string = [self.stringClass stringWithString: whitespace[0]]; + [string deleteTrailingWhitespaces]; + OTAssertEqualObjects(string, @" \r \t\n\t \tasd"); + + string = [self.stringClass stringWithString: whitespace[1]]; + [string deleteTrailingWhitespaces]; + OTAssertEqualObjects(string, @""); +} + +- (void)deleteEnclosingWhitespaces +{ + OFMutableString *string; + + string = [self.stringClass stringWithString: whitespace[0]]; + [string deleteEnclosingWhitespaces]; + OTAssertEqualObjects(string, @"asd"); + + string = [self.stringClass stringWithString: whitespace[1]]; + [string deleteEnclosingWhitespaces]; + OTAssertEqualObjects(string, @""); +} +@end + +@implementation CustomMutableString +- (instancetype)init +{ + self = [super init]; + + @try { + _string = [[OFMutableString alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)initWithString: (OFString *)string +{ + self = [super init]; + + @try { + _string = [string mutableCopy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)initWithCString: (const char *)cString + encoding: (OFStringEncoding)encoding + length: (size_t)length +{ + self = [super init]; + + @try { + _string = [[OFMutableString alloc] initWithCString: cString + encoding: encoding + length: length]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)initWithUTF16String: (const OFChar16 *)UTF16String + length: (size_t)length + byteOrder: (OFByteOrder)byteOrder +{ + self = [super init]; + + @try { + _string = [[OFMutableString alloc] + initWithUTF16String: UTF16String + length: length + byteOrder: byteOrder]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)initWithUTF32String: (const OFChar32 *)UTF32String + length: (size_t)length + byteOrder: (OFByteOrder)byteOrder +{ + self = [super init]; + + @try { + _string = [[OFMutableString alloc] + initWithUTF32String: UTF32String + length: length + byteOrder: byteOrder]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (instancetype)initWithFormat: (OFConstantString *)format + arguments: (va_list)arguments +{ + self = [super init]; + + @try { + _string = [[OFMutableString alloc] initWithFormat: format + arguments: arguments]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_string release]; + + [super dealloc]; +} + +- (OFUnichar)characterAtIndex: (size_t)idx +{ + return [_string characterAtIndex: idx]; +} + +- (size_t)length +{ + return _string.length; +} + +- (void)replaceCharactersInRange: (OFRange)range + withString: (OFString *)string +{ + [_string replaceCharactersInRange: range withString: string]; +} +@end ADDED tests/OFMutableUTF8StringTests.m Index: tests/OFMutableUTF8StringTests.m ================================================================== --- /dev/null +++ tests/OFMutableUTF8StringTests.m @@ -0,0 +1,30 @@ +/* + * 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 "OFMutableStringTests.h" + +#import "OFMutableUTF8String.h" + +@interface OFMutableUTF8StringTests: OFMutableStringTests +@end + +@implementation OFMutableUTF8StringTests +- (Class)arrayClass +{ + return [OFMutableUTF8String class]; +} +@end Index: tests/OFNotificationCenterTests.m ================================================================== --- tests/OFNotificationCenterTests.m +++ tests/OFNotificationCenterTests.m @@ -13,29 +13,32 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFNotificationCenter"; static const OFNotificationName notificationName = @"OFNotificationCenterTestName"; static const OFNotificationName otherNotificationName = @"OFNotificationCenterTestOtherName"; -@interface OFNotificationCenterTest: OFObject +@interface OFNotificationCenterTests: OTTestCase +@end + +@interface OFNotificationCenterTestClass: OFObject { @public id _expectedObject; int _received; } - (void)handleNotification: (OFNotification *)notification; @end -@implementation OFNotificationCenterTest +@implementation OFNotificationCenterTestClass - (void)handleNotification: (OFNotification *)notification { OFEnsure([notification.name isEqual: notificationName]); OFEnsure(_expectedObject == nil || notification.object == _expectedObject); @@ -42,118 +45,117 @@ _received++; } @end -@implementation TestsAppDelegate (OFNotificationCenterTests) -- (void)notificationCenterTests +@implementation OFNotificationCenterTests +- (void)testNotificationCenter { - void *pool = objc_autoreleasePoolPush(); OFNotificationCenter *center = [OFNotificationCenter defaultCenter]; - OFNotificationCenterTest *test1, *test2, *test3, *test4; + OFNotificationCenterTestClass *test1, *test2, *test3, *test4; OFNotification *notification; - test1 = - [[[OFNotificationCenterTest alloc] init] autorelease]; + test1 = [[[OFNotificationCenterTestClass alloc] init] autorelease]; test1->_expectedObject = self; - test2 = - [[[OFNotificationCenterTest alloc] init] autorelease]; - test3 = - [[[OFNotificationCenterTest alloc] init] autorelease]; + test2 = [[[OFNotificationCenterTestClass alloc] init] autorelease]; + test3 = [[[OFNotificationCenterTestClass alloc] init] autorelease]; test3->_expectedObject = self; - test4 = - [[[OFNotificationCenterTest alloc] init] autorelease]; + test4 = [[[OFNotificationCenterTestClass alloc] init] autorelease]; /* First one intentionally added twice to test deduplication. */ - TEST(@"-[addObserver:selector:name:object:]", - R([center addObserver: test1 - selector: @selector(handleNotification:) - name: notificationName - object: self]) && - R([center addObserver: test1 - selector: @selector(handleNotification:) - name: notificationName - object: self]) && - R([center addObserver: test2 - selector: @selector(handleNotification:) - name: notificationName - object: nil]) && - R([center addObserver: test3 - selector: @selector(handleNotification:) - name: otherNotificationName - object: self]) && - R([center addObserver: test4 - selector: @selector(handleNotification:) - name: otherNotificationName - object: nil])) + [center addObserver: test1 + selector: @selector(handleNotification:) + name: notificationName + object: self]; + [center addObserver: test1 + selector: @selector(handleNotification:) + name: notificationName + object: self]; + [center addObserver: test2 + selector: @selector(handleNotification:) + name: notificationName + object: nil]; + [center addObserver: test3 + selector: @selector(handleNotification:) + name: otherNotificationName + object: self]; + [center addObserver: test4 + selector: @selector(handleNotification:) + name: otherNotificationName + object: nil]; notification = [OFNotification notificationWithName: notificationName object: nil]; - TEST(@"-[postNotification:] #1", - R([center postNotification: notification]) && - test1->_received == 0 && test2->_received == 1 && - test3->_received == 0 && test4->_received == 0) + [center postNotification: notification]; + OTAssertEqual(test1->_received, 0); + OTAssertEqual(test2->_received, 1); + OTAssertEqual(test3->_received, 0); + OTAssertEqual(test4->_received, 0); notification = [OFNotification notificationWithName: notificationName object: self]; - TEST(@"-[postNotification:] #2", - R([center postNotification: notification]) && - test1->_received == 1 && test2->_received == 2 && - test3->_received == 0 && test4->_received == 0) + [center postNotification: notification]; + OTAssertEqual(test1->_received, 1); + OTAssertEqual(test2->_received, 2); + OTAssertEqual(test3->_received, 0); + OTAssertEqual(test4->_received, 0); notification = [OFNotification notificationWithName: notificationName object: @"foo"]; - TEST(@"-[postNotification:] #3", - R([center postNotification: notification]) && - test1->_received == 1 && test2->_received == 3 && - test3->_received == 0 && test4->_received == 0) + [center postNotification: notification]; + OTAssertEqual(test1->_received, 1); + OTAssertEqual(test2->_received, 3); + OTAssertEqual(test3->_received, 0); + OTAssertEqual(test4->_received, 0); #ifdef OF_HAVE_BLOCKS __block bool received = false; id handle; notification = [OFNotification notificationWithName: notificationName object: self]; - TEST(@"-[addObserverForName:object:usingBlock:]", - (handle = [center addObserverForName: notificationName - object: self - usingBlock: ^ (OFNotification *notif) { - OFEnsure(notif == notification && !received); + handle = [center addObserverForName: notificationName + object: self + usingBlock: ^ (OFNotification *notification_) { + OTAssertEqual(notification_, notification); + OTAssertFalse(received); received = true; - }]) && R([center postNotification: notification]) && received && - test1->_received == 2 && test2->_received == 4 && - test3->_received == 0 && test4->_received == 0) + }]; + [center postNotification: notification]; + OTAssertTrue(received); + OTAssertEqual(test1->_received, 2); + OTAssertEqual(test2->_received, 4); + OTAssertEqual(test3->_received, 0); + OTAssertEqual(test4->_received, 0); /* Act like the block test didn't happen. */ [center removeObserver: handle]; test1->_received--; test2->_received--; #endif - TEST(@"-[removeObserver:selector:name:object:]", - R([center removeObserver: test1 - selector: @selector(handleNotification:) - name: notificationName - object: self]) && - R([center removeObserver: test2 - selector: @selector(handleNotification:) - name: notificationName - object: nil]) && - R([center removeObserver: test3 - selector: @selector(handleNotification:) - name: otherNotificationName - object: self]) && - R([center removeObserver: test4 - selector: @selector(handleNotification:) - name: otherNotificationName - object: nil])) + [center removeObserver: test1 + selector: @selector(handleNotification:) + name: notificationName + object: self]; + [center removeObserver: test2 + selector: @selector(handleNotification:) + name: notificationName + object: nil]; + [center removeObserver: test3 + selector: @selector(handleNotification:) + name: otherNotificationName + object: self]; + [center removeObserver: test4 + selector: @selector(handleNotification:) + name: otherNotificationName + object: nil]; notification = [OFNotification notificationWithName: notificationName object: self]; - TEST(@"-[postNotification:] with no observers", - R([center postNotification: notification]) && - test1->_received == 1 && test2->_received == 3 && - test3->_received == 0 && test4->_received == 0) - - objc_autoreleasePoolPop(pool); + [center postNotification: notification]; + OTAssertEqual(test1->_received, 1); + OTAssertEqual(test2->_received, 3); + OTAssertEqual(test3->_received, 0); + OTAssertEqual(test4->_received, 0); } @end Index: tests/OFNumberTests.m ================================================================== --- tests/OFNumberTests.m +++ tests/OFNumberTests.m @@ -13,80 +13,118 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFNumber"; - -@implementation TestsAppDelegate (OFNumberTests) -- (void)numberTests -{ - void *pool = objc_autoreleasePoolPush(); - OFNumber *number; - - TEST(@"+[numberWithLongLong:]", - (number = [OFNumber numberWithLongLong: 123456789])) - - TEST(@"-[isEqual:]", - [number isEqual: [OFNumber numberWithLong: 123456789]]) - - TEST(@"-[hash]", number.hash == 0x82D8BC42) - - TEST(@"-[charValue]", number.charValue == 21) - - TEST(@"-[doubleValue]", number.doubleValue == 123456789.L) - - TEST(@"signed char minimum & maximum unmodified", - (number = [OFNumber numberWithChar: SCHAR_MIN]) && - number.charValue == SCHAR_MIN && - (number = [OFNumber numberWithChar: SCHAR_MAX]) && - number.charValue == SCHAR_MAX) - - TEST(@"short minimum & maximum unmodified", - (number = [OFNumber numberWithShort: SHRT_MIN]) && - number.shortValue == SHRT_MIN && - (number = [OFNumber numberWithShort: SHRT_MAX]) && - number.shortValue == SHRT_MAX) - - TEST(@"int minimum & maximum unmodified", - (number = [OFNumber numberWithInt: INT_MIN]) && - number.intValue == INT_MIN && - (number = [OFNumber numberWithInt: INT_MAX]) && - number.intValue == INT_MAX) - - TEST(@"long minimum & maximum unmodified", - (number = [OFNumber numberWithLong: LONG_MIN]) && - number.longValue == LONG_MIN && - (number = [OFNumber numberWithLong: LONG_MAX]) && - number.longValue == LONG_MAX) - - TEST(@"long long minimum & maximum unmodified", - (number = [OFNumber numberWithLongLong: LLONG_MIN]) && - number.longLongValue == LLONG_MIN && - (number = [OFNumber numberWithLongLong: LLONG_MAX]) && - number.longLongValue == LLONG_MAX) - - TEST(@"unsigned char maximum unmodified", - (number = [OFNumber numberWithUnsignedChar: UCHAR_MAX]) && - number.unsignedCharValue == UCHAR_MAX) - - TEST(@"unsigned short maximum unmodified", - (number = [OFNumber numberWithUnsignedShort: USHRT_MAX]) && - number.unsignedShortValue == USHRT_MAX) - - TEST(@"unsigned int maximum unmodified", - (number = [OFNumber numberWithUnsignedInt: UINT_MAX]) && - number.unsignedIntValue == UINT_MAX) - - TEST(@"unsigned long maximum unmodified", - (number = [OFNumber numberWithUnsignedLong: ULONG_MAX]) && - number.unsignedLongValue == ULONG_MAX) - - TEST(@"unsigned long long maximum unmodified", - (number = [OFNumber numberWithUnsignedLongLong: ULLONG_MAX]) && - number.unsignedLongLongValue == ULLONG_MAX) - - objc_autoreleasePoolPop(pool); +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFNumberTests: OTTestCase +{ + OFNumber *_number; +} +@end + +@implementation OFNumberTests +- (void)setUp +{ + [super setUp]; + + _number = [[OFNumber alloc] initWithLongLong: 123456789]; +} + +- (void)dealloc +{ + [_number release]; + + [super dealloc]; +} + +- (void)testIsEqual +{ + OTAssertEqualObjects(_number, [OFNumber numberWithLong: 123456789]); +} + +- (void)testHash +{ + OTAssertEqual(_number.hash, + [[OFNumber numberWithLong: 123456789] hash]); +} + +- (void)testCharValue +{ + OTAssertEqual(_number.charValue, 21); +} + +- (void)testDoubleValue +{ + OTAssertEqual(_number.doubleValue, 123456789.L); +} + +- (void)testSignedCharMinAndMaxUnmodified +{ + OTAssertEqual([[OFNumber numberWithChar: SCHAR_MIN] charValue], + SCHAR_MIN); + OTAssertEqual([[OFNumber numberWithChar: SCHAR_MAX] charValue], + SCHAR_MAX); +} + +- (void)testShortMinAndMaxUnmodified +{ + OTAssertEqual([[OFNumber numberWithShort: SHRT_MIN] shortValue], + SHRT_MIN); + OTAssertEqual([[OFNumber numberWithShort: SHRT_MAX] shortValue], + SHRT_MAX); +} + +- (void)testIntMinAndMaxUnmodified +{ + OTAssertEqual([[OFNumber numberWithInt: INT_MIN] intValue], INT_MIN); + OTAssertEqual([[OFNumber numberWithInt: INT_MAX] intValue], INT_MAX); +} + +- (void)testLongMinAndMaxUnmodified +{ + OTAssertEqual([[OFNumber numberWithLong: LONG_MIN] longValue], + LONG_MIN); + OTAssertEqual([[OFNumber numberWithLong: LONG_MAX] longValue], + LONG_MAX);; +} + +- (void)testLongLongMinAndMaxUnmodified +{ + OTAssertEqual([[OFNumber numberWithLongLong: LLONG_MIN] longLongValue], + LLONG_MIN); + OTAssertEqual([[OFNumber numberWithLongLong: LLONG_MAX] longLongValue], + LLONG_MAX); +} + +- (void)testUnsignedCharMaxUnmodified +{ + OTAssertEqual([[OFNumber numberWithUnsignedChar: UCHAR_MAX] + unsignedCharValue], UCHAR_MAX); +} + +- (void)testUnsignedShortMaxUnmodified +{ + OTAssertEqual([[OFNumber numberWithUnsignedShort: USHRT_MAX] + unsignedShortValue], USHRT_MAX); +} + +- (void)testUnsignedIntMaxUnmodified +{ + OTAssertEqual([[OFNumber numberWithUnsignedInt: UINT_MAX] + unsignedIntValue], UINT_MAX); +} + +- (void)testUnsignedLongMaxUnmodified +{ + OTAssertEqual([[OFNumber numberWithUnsignedLong: ULONG_MAX] + unsignedLongValue], ULONG_MAX); +} + +- (void)testUnsignedLongLongMaxUnmodified +{ + OTAssertEqual([[OFNumber numberWithUnsignedLongLong: ULLONG_MAX] + unsignedLongLongValue], ULLONG_MAX); } @end Index: tests/OFObjectTests.m ================================================================== --- tests/OFObjectTests.m +++ tests/OFObjectTests.m @@ -13,19 +13,12 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -#if (defined(OF_DRAGONFLYBSD) && defined(__LP64__)) || defined(OF_NINTENDO_3DS) -# define TOO_BIG (SIZE_MAX / 3) -#else -# define TOO_BIG (SIZE_MAX - 128) -#endif - -static OFString *const module = @"OFObject"; +#import "ObjFW.h" +#import "ObjFWTest.h" @interface MyObject: OFObject { id _objectValue; Class _classValue; @@ -58,10 +51,196 @@ @property (nonatomic) unsigned long unsignedLongValue; @property (nonatomic) unsigned long long unsignedLongLongValue; @property (nonatomic) float floatValue; @property (nonatomic) double doubleValue; @end + +@interface OFObjectTests: OTTestCase +{ + MyObject *_myObject; +} +@end + +@implementation OFObjectTests +- (void)setUp +{ + [super setUp]; + + _myObject = [[MyObject alloc] init]; +} + +- (void)dealloc +{ + [_myObject release]; + + [super dealloc]; +} + +- (void)testClassDescription +{ + OTAssertEqualObjects([OFObject description], @"OFObject"); + OTAssertEqualObjects([MyObject description], @"MyObject"); +} + +- (void)testInstanceDescription +{ + OFObject *object = [[[OFObject alloc] init] autorelease]; + + OTAssertEqualObjects(object.description, @""); + OTAssertEqualObjects(_myObject.description, @""); +} + +- (void)testValueForKey +{ + _myObject.objectValue = @"Hello"; + _myObject.classValue = _myObject.class; + + OTAssertEqualObjects([_myObject valueForKey: @"objectValue"], @"Hello"); + OTAssertEqualObjects([_myObject valueForKey: @"classValue"], + _myObject.class); + OTAssertEqualObjects([_myObject valueForKey: @"class"], + _myObject.class); +} + +- (void)testValueForKeyWithUndefinedKeyThrows +{ + OTAssertThrowsSpecific([_myObject valueForKey: @"undefined"], + OFUndefinedKeyException); +} + +- (void)testSetValueForKey +{ + [_myObject setValue: @"World" forKey: @"objectValue"]; + [_myObject setValue: [OFObject class] forKey: @"classValue"]; + + OTAssertEqualObjects(_myObject.objectValue, @"World"); + OTAssertEqualObjects(_myObject.classValue, [OFObject class]); +} + +- (void)testSetValueWithUndefinedKeyThrows +{ + OTAssertThrowsSpecific([_myObject setValue: @"x" forKey: @"undefined"], + OFUndefinedKeyException); +} + +- (void)testAutoWrappingOfValueForKey +{ + _myObject.boolValue = 1; + _myObject.charValue = 2; + _myObject.shortValue = 3; + _myObject.intValue = 4; + _myObject.longValue = 5; + _myObject.longLongValue = 6; + _myObject.unsignedCharValue = 7; + _myObject.unsignedShortValue = 8; + _myObject.unsignedIntValue = 9; + _myObject.unsignedLongValue = 10; + _myObject.unsignedLongLongValue = 11; + _myObject.floatValue = 12; + _myObject.doubleValue = 13; + + OTAssertEqualObjects([_myObject valueForKey: @"boolValue"], + [OFNumber numberWithBool: 1]); + OTAssertEqualObjects([_myObject valueForKey: @"charValue"], + [OFNumber numberWithChar: 2]); + OTAssertEqualObjects([_myObject valueForKey: @"shortValue"], + [OFNumber numberWithShort: 3]); + OTAssertEqualObjects([_myObject valueForKey: @"intValue"], + [OFNumber numberWithInt: 4]); + OTAssertEqualObjects([_myObject valueForKey: @"longValue"], + [OFNumber numberWithLong: 5]); + OTAssertEqualObjects([_myObject valueForKey: @"longLongValue"], + [OFNumber numberWithLongLong: 6]); + OTAssertEqualObjects([_myObject valueForKey: @"unsignedCharValue"], + [OFNumber numberWithUnsignedChar: 7]); + OTAssertEqualObjects([_myObject valueForKey: @"unsignedShortValue"], + [OFNumber numberWithUnsignedShort: 8]); + OTAssertEqualObjects([_myObject valueForKey: @"unsignedIntValue"], + [OFNumber numberWithUnsignedInt: 9]); + OTAssertEqualObjects([_myObject valueForKey: @"unsignedLongValue"], + [OFNumber numberWithUnsignedLong: 10]); + OTAssertEqualObjects([_myObject valueForKey: @"unsignedLongLongValue"], + [OFNumber numberWithUnsignedLongLong: 11]); + OTAssertEqualObjects([_myObject valueForKey: @"floatValue"], + [OFNumber numberWithFloat: 12]); + OTAssertEqualObjects([_myObject valueForKey: @"doubleValue"], + [OFNumber numberWithDouble: 13]); +} + +- (void)testAutoWrappingOfSetValueForKey +{ + [_myObject setValue: [OFNumber numberWithBool: 0] + forKey: @"boolValue"]; + [_myObject setValue: [OFNumber numberWithChar: 10] + forKey: @"charValue"]; + [_myObject setValue: [OFNumber numberWithShort: 20] + forKey: @"shortValue"]; + [_myObject setValue: [OFNumber numberWithInt: 30] + forKey: @"intValue"]; + [_myObject setValue: [OFNumber numberWithLong: 40] + forKey: @"longValue"]; + [_myObject setValue: [OFNumber numberWithLongLong: 50] + forKey: @"longLongValue"]; + [_myObject setValue: [OFNumber numberWithUnsignedChar: 60] + forKey: @"unsignedCharValue"]; + [_myObject setValue: [OFNumber numberWithUnsignedShort: 70] + forKey: @"unsignedShortValue"]; + [_myObject setValue: [OFNumber numberWithUnsignedInt: 80] + forKey: @"unsignedIntValue"]; + [_myObject setValue: [OFNumber numberWithUnsignedLong: 90] + forKey: @"unsignedLongValue"]; + [_myObject setValue: [OFNumber numberWithUnsignedLongLong: 100] + forKey: @"unsignedLongLongValue"]; + [_myObject setValue: [OFNumber numberWithFloat: 110] + forKey: @"floatValue"]; + [_myObject setValue: [OFNumber numberWithDouble: 120] + forKey: @"doubleValue"]; + + OTAssertEqual(_myObject.isBoolValue, 0); + OTAssertEqual(_myObject.charValue, 10); + OTAssertEqual(_myObject.shortValue, 20); + OTAssertEqual(_myObject.intValue, 30); + OTAssertEqual(_myObject.longValue, 40); + OTAssertEqual(_myObject.longLongValue, 50); + OTAssertEqual(_myObject.unsignedCharValue, 60); + OTAssertEqual(_myObject.unsignedShortValue, 70); + OTAssertEqual(_myObject.unsignedIntValue, 80); + OTAssertEqual(_myObject.unsignedLongValue, 90); + OTAssertEqual(_myObject.unsignedLongLongValue, 100); + OTAssertEqual(_myObject.floatValue, 110); + OTAssertEqual(_myObject.doubleValue, 120); +} + +- (void)testSetValueForKeyWithNilThrows +{ + OTAssertThrowsSpecific( + [_myObject setValue: (id _Nonnull)nil forKey: @"intValue"], + OFInvalidArgumentException); +} + +- (void)testValueForKeyPath +{ + _myObject.objectValue = [[[MyObject alloc] init] autorelease]; + [_myObject.objectValue setObjectValue: + [[[MyObject alloc] init] autorelease]]; + [[_myObject.objectValue objectValue] setDoubleValue: 0.5]; + + OTAssertEqual([[_myObject valueForKeyPath: + @"objectValue.objectValue.doubleValue"] doubleValue], 0.5); +} + +- (void)testSetValueForKeyPath +{ + _myObject.objectValue = [[[MyObject alloc] init] autorelease]; + [_myObject.objectValue setObjectValue: + [[[MyObject alloc] init] autorelease]]; + [_myObject setValue: [OFNumber numberWithDouble: 0.75] + forKeyPath: @"objectValue.objectValue.doubleValue"]; + + OTAssertEqual([[_myObject.objectValue objectValue] doubleValue], 0.75); +} +@end @implementation MyObject @synthesize objectValue = _objectValue, classValue = _classValue; @synthesize boolValue = _boolValue, charValue = _charValue; @synthesize shortValue = _shortValue, intValue = _intValue; @@ -77,145 +256,6 @@ { [_objectValue release]; [super dealloc]; } -@end - -@implementation TestsAppDelegate (OFObjectTests) -- (void)objectTests -{ - void *pool = objc_autoreleasePoolPush(); - OFObject *object; - MyObject *myObject; - - TEST(@"+[description]", - [[OFObject description] isEqual: @"OFObject"] && - [[MyObject description] isEqual: @"MyObject"]) - - object = [[[OFObject alloc] init] autorelease]; - myObject = [[[MyObject alloc] init] autorelease]; - - TEST(@"-[description]", - [object.description isEqual: @""] && - [myObject.description isEqual: @""]) - - myObject.objectValue = @"Hello"; - myObject.classValue = myObject.class; - TEST(@"-[valueForKey:]", - [[myObject valueForKey: @"objectValue"] isEqual: @"Hello"] && - [[myObject valueForKey: @"classValue"] isEqual: myObject.class] && - [[myObject valueForKey: @"class"] isEqual: myObject.class]) - - EXPECT_EXCEPTION(@"-[valueForKey:] with undefined key", - OFUndefinedKeyException, [myObject valueForKey: @"undefined"]) - - TEST(@"-[setValue:forKey:]", - R([myObject setValue: @"World" forKey: @"objectValue"]) && - R([myObject setValue: [OFObject class] forKey: @"classValue"]) && - [myObject.objectValue isEqual: @"World"] && - [myObject.classValue isEqual: [OFObject class]]) - - EXPECT_EXCEPTION(@"-[setValue:forKey:] with undefined key", - OFUndefinedKeyException, - [myObject setValue: @"x" forKey: @"undefined"]) - - myObject.boolValue = 1; - myObject.charValue = 2; - myObject.shortValue = 3; - myObject.intValue = 4; - myObject.longValue = 5; - myObject.longLongValue = 6; - myObject.unsignedCharValue = 7; - myObject.unsignedShortValue = 8; - myObject.unsignedIntValue = 9; - myObject.unsignedLongValue = 10; - myObject.unsignedLongLongValue = 11; - myObject.floatValue = 12; - myObject.doubleValue = 13; - TEST(@"Auto-wrapping of -[valueForKey:]", - [[myObject valueForKey: @"boolValue"] isEqual: - [OFNumber numberWithBool: 1]] && - [[myObject valueForKey: @"charValue"] isEqual: - [OFNumber numberWithChar: 2]] && - [[myObject valueForKey: @"shortValue"] isEqual: - [OFNumber numberWithShort: 3]] && - [[myObject valueForKey: @"intValue"] isEqual: - [OFNumber numberWithInt: 4]] && - [[myObject valueForKey: @"longValue"] isEqual: - [OFNumber numberWithLong: 5]] && - [[myObject valueForKey: @"longLongValue"] isEqual: - [OFNumber numberWithLongLong: 6]] && - [[myObject valueForKey: @"unsignedCharValue"] isEqual: - [OFNumber numberWithUnsignedChar: 7]] && - [[myObject valueForKey: @"unsignedShortValue"] isEqual: - [OFNumber numberWithUnsignedShort: 8]] && - [[myObject valueForKey: @"unsignedIntValue"] isEqual: - [OFNumber numberWithUnsignedInt: 9]] && - [[myObject valueForKey: @"unsignedLongValue"] isEqual: - [OFNumber numberWithUnsignedLong: 10]] && - [[myObject valueForKey: @"unsignedLongLongValue"] isEqual: - [OFNumber numberWithUnsignedLongLong: 11]] && - [[myObject valueForKey: @"floatValue"] isEqual: - [OFNumber numberWithFloat: 12]] && - [[myObject valueForKey: @"doubleValue"] isEqual: - [OFNumber numberWithDouble: 13]]) - - TEST(@"Auto-wrapping of -[setValue:forKey:]", - R([myObject setValue: [OFNumber numberWithBool: 0] - forKey: @"boolValue"]) && - R([myObject setValue: [OFNumber numberWithChar: 10] - forKey: @"charValue"]) && - R([myObject setValue: [OFNumber numberWithShort: 20] - forKey: @"shortValue"]) && - R([myObject setValue: [OFNumber numberWithInt: 30] - forKey: @"intValue"]) && - R([myObject setValue: [OFNumber numberWithLong: 40] - forKey: @"longValue"]) && - R([myObject setValue: [OFNumber numberWithLongLong: 50] - forKey: @"longLongValue"]) && - R([myObject setValue: [OFNumber numberWithUnsignedChar: 60] - forKey: @"unsignedCharValue"]) && - R([myObject setValue: [OFNumber numberWithUnsignedShort: 70] - forKey: @"unsignedShortValue"]) && - R([myObject setValue: [OFNumber numberWithUnsignedInt: 80] - forKey: @"unsignedIntValue"]) && - R([myObject setValue: [OFNumber numberWithUnsignedLong: 90] - forKey: @"unsignedLongValue"]) && - R([myObject setValue: [OFNumber numberWithUnsignedLongLong: 100] - forKey: @"unsignedLongLongValue"]) && - R([myObject setValue: [OFNumber numberWithFloat: 110] - forKey: @"floatValue"]) && - R([myObject setValue: [OFNumber numberWithDouble: 120] - forKey: @"doubleValue"]) && - myObject.isBoolValue == 0 && myObject.charValue == 10 && - myObject.shortValue == 20 && myObject.intValue == 30 && - myObject.longValue == 40 && myObject.longLongValue == 50 && - myObject.unsignedCharValue == 60 && - myObject.unsignedShortValue == 70 && - myObject.unsignedIntValue == 80 && - myObject.unsignedLongValue == 90 && - myObject.unsignedLongLongValue == 100 && - myObject.floatValue == 110 && - myObject.doubleValue == 120) - - EXPECT_EXCEPTION(@"Catch -[setValue:forKey:] with nil key for scalar", - OFInvalidArgumentException, - [myObject setValue: (id _Nonnull)nil forKey: @"intValue"]) - - TEST(@"-[valueForKeyPath:]", - (myObject = [[[MyObject alloc] init] autorelease]) && - (myObject.objectValue = [[[MyObject alloc] init] autorelease]) && - R([myObject.objectValue - setObjectValue: [[[MyObject alloc] init] autorelease]]) && - R([[myObject.objectValue objectValue] setDoubleValue: 0.5]) && - [[myObject valueForKeyPath: @"objectValue.objectValue.doubleValue"] - doubleValue] == 0.5) - - TEST(@"[-setValue:forKeyPath:]", - R([myObject setValue: [OFNumber numberWithDouble: 0.75] - forKeyPath: @"objectValue.objectValue.doubleValue"]) && - [[myObject.objectValue objectValue] doubleValue] == 0.75) - - objc_autoreleasePoolPop(pool); -} @end Index: tests/OFPBKDF2Tests.m ================================================================== --- tests/OFPBKDF2Tests.m +++ tests/OFPBKDF2Tests.m @@ -15,111 +15,156 @@ #include "config.h" #include -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFPBKDF2"; - -@implementation TestsAppDelegate (OFPBKDF2Tests) -- (void)PBKDF2Tests -{ - void *pool = objc_autoreleasePoolPush(); - OFHMAC *HMAC = [OFHMAC HMACWithHashClass: [OFSHA1Hash class] - allowsSwappableMemory: true]; +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFPBKDF2Tests: OTTestCase +{ + OFHMAC *_HMAC; +} +@end + +@implementation OFPBKDF2Tests +- (void)setUp +{ + [super setUp]; + + _HMAC = [[OFHMAC alloc] initWithHashClass: [OFSHA1Hash class] + allowsSwappableMemory: true]; +} + +- (void)dealloc +{ + [_HMAC release]; + + [super dealloc]; +} + +/* Test vectors from RFC 6070 */ + +- (void)testRFC6070TestVector1 +{ unsigned char key[25]; - /* Test vectors from RFC 6070 */ - - TEST(@"PBKDF2-SHA1, 1 iteration", - R(OFPBKDF2((OFPBKDF2Parameters){ - .HMAC = HMAC, + OFPBKDF2((OFPBKDF2Parameters){ + .HMAC = _HMAC, .iterations = 1, .salt = (unsigned char *)"salt", .saltLength = 4, .password = "password", .passwordLength = 8, .key = key, .keyLength = 20, .allowsSwappableMemory = true - })) && memcmp(key, "\x0C\x60\xC8\x0F\x96\x1F\x0E\x71\xF3\xA9\xB5" - "\x24\xAF\x60\x12\x06\x2F\xE0\x37\xA6", 20) == 0) + }); + + OTAssertEqual(memcmp(key, "\x0C\x60\xC8\x0F\x96\x1F\x0E\x71\xF3\xA9\xB5" + "\x24\xAF\x60\x12\x06\x2F\xE0\x37\xA6", 20), 0); +} + +- (void)testRFC6070TestVector2 +{ + unsigned char key[25]; - TEST(@"PBKDF2-SHA1, 2 iterations", - R(OFPBKDF2((OFPBKDF2Parameters){ - .HMAC = HMAC, + OFPBKDF2((OFPBKDF2Parameters){ + .HMAC = _HMAC, .iterations = 2, .salt = (unsigned char *)"salt", .saltLength = 4, .password = "password", .passwordLength = 8, .key = key, .keyLength = 20, .allowsSwappableMemory = true - })) && memcmp(key, "\xEA\x6C\x01\x4D\xC7\x2D\x6F\x8C\xCD\x1E\xD9" - "\x2A\xCE\x1D\x41\xF0\xD8\xDE\x89\x57", 20) == 0) + }); + + OTAssertEqual(memcmp(key, "\xEA\x6C\x01\x4D\xC7\x2D\x6F\x8C\xCD\x1E\xD9" + "\x2A\xCE\x1D\x41\xF0\xD8\xDE\x89\x57", 20), 0); +} + +- (void)testRFC6070TestVector3 +{ + unsigned char key[25]; - TEST(@"PBKDF2-SHA1, 4096 iterations", - R(OFPBKDF2((OFPBKDF2Parameters){ - .HMAC = HMAC, + OFPBKDF2((OFPBKDF2Parameters){ + .HMAC = _HMAC, .iterations = 4096, .salt = (unsigned char *)"salt", .saltLength = 4, .password = "password", .passwordLength = 8, .key = key, .keyLength = 20, .allowsSwappableMemory = true - })) && memcmp(key, "\x4B\x00\x79\x01\xB7\x65\x48\x9A\xBE\xAD\x49" - "\xD9\x26\xF7\x21\xD0\x65\xA4\x29\xC1", 20) == 0) + }); - /* This test takes too long, even on a fast machine. */ + OTAssertEqual(memcmp(key, "\x4B\x00\x79\x01\xB7\x65\x48\x9A\xBE\xAD\x49" + "\xD9\x26\xF7\x21\xD0\x65\xA4\x29\xC1", 20), 0); +} + #if 0 - TEST(@"PBKDF2-SHA1, 16777216 iterations", - R(OFPBKDF2((OFPBKDF2Parameters){ - .HMAC = HMAC, +/* This test takes too long, even on a fast machine. */ +- (void)testRFC6070TestVector4 +{ + unsigned char key[25]; + + OFPBKDF2((OFPBKDF2Parameters){ + .HMAC = _HMAC, .iterations = 16777216, .salt = (unsigned char *)"salt", .saltLength = 4, .password = "password", .passwordLength = 8, .key = key, .keyLength = 20, .allowsSwappableMemory = true - })) && memcmp(key, "\xEE\xFE\x3D\x61\xCD\x4D\xA4\xE4\xE9\x94\x5B" - "\x3D\x6B\xA2\x15\x8C\x26\x34\xE9\x84", 20) == 0) + }); + + OTAssertEqual(memcmp(key, "\xEE\xFE\x3D\x61\xCD\x4D\xA4\xE4\xE9\x94\x5B" + "\x3D\x6B\xA2\x15\x8C\x26\x34\xE9\x84", 20), 0); +} #endif - TEST(@"PBKDF2-SHA1, 4096 iterations, key > 1 block", - R(OFPBKDF2((OFPBKDF2Parameters){ - .HMAC = HMAC, +- (void)testRFC6070TestVector5 +{ + unsigned char key[25]; + + OFPBKDF2((OFPBKDF2Parameters){ + .HMAC = _HMAC, .iterations = 4096, .salt = (unsigned char *)"saltSALTsaltSALTsalt" - "SALTsaltSALTsalt", + "SALTsaltSALTsalt", .saltLength = 36, .password = "passwordPASSWORDpassword", .passwordLength = 24, .key = key, .keyLength = 25, .allowsSwappableMemory = true - })) && - memcmp(key, "\x3D\x2E\xEC\x4F\xE4\x1C\x84\x9B\x80\xC8\xD8\x36\x62" - "\xC0\xE4\x4A\x8B\x29\x1A\x96\x4C\xF2\xF0\x70\x38", 25) == 0) + }); + + OTAssertEqual(memcmp(key, "\x3D\x2E\xEC\x4F\xE4\x1C\x84\x9B\x80\xC8\xD8" + "\x36\x62\xC0\xE4\x4A\x8B\x29\x1A\x96\x4C\xF2\xF0\x70\x38", 25), 0); +} + +- (void)testRFC6070TestVector6 +{ + unsigned char key[25]; - TEST(@"PBKDF2-SHA1, 4096 iterations, key < 1 block", - R(OFPBKDF2((OFPBKDF2Parameters){ - .HMAC = HMAC, + OFPBKDF2((OFPBKDF2Parameters){ + .HMAC = _HMAC, .iterations = 4096, .salt = (unsigned char *)"sa\0lt", .saltLength = 5, .password = "pass\0word", .passwordLength = 9, .key = key, .keyLength = 16, .allowsSwappableMemory = true - })) && memcmp(key, "\x56\xFA\x6A\xA7\x55\x48\x09\x9D\xCC\x37\xD7" - "\xF0\x34\x25\xE0\xC3", 16) == 0) + }); - objc_autoreleasePoolPop(pool); + OTAssertEqual(memcmp(key, "\x56\xFA\x6A\xA7\x55\x48\x09\x9D\xCC\x37\xD7" + "\xF0\x34\x25\xE0\xC3", 16), 0); } @end Index: tests/OFPluginTests.m ================================================================== --- tests/OFPluginTests.m +++ tests/OFPluginTests.m @@ -13,44 +13,42 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" #import "plugin/TestPlugin.h" -#ifndef OF_IOS -static OFString *const pluginName = @"plugin/TestPlugin"; -#else -static OFString *const pluginName = @"PlugIns/TestPlugin"; -#endif - -static OFString *const module = @"OFPlugin"; - -@implementation TestsAppDelegate (OFPluginTests) -- (void)pluginTests -{ - void *pool = objc_autoreleasePoolPush(); +@interface OFPluginTests: OTTestCase +@end + +@implementation OFPluginTests +- (void)testPlugin +{ + TestPlugin *test = nil; OFString *path; OFPlugin *plugin; Class (*class)(void); - TestPlugin *test; - - TEST(@"+[pathForName:]", (path = [OFPlugin pathForName: pluginName])) - - TEST(@"+[pluginWithPath:]", (plugin = [OFPlugin pluginWithPath: path])) - - TEST(@"-[addressForSymbol:]", - (class = (Class (*)(void))(uintptr_t) - [plugin addressForSymbol: @"class"])) - - test = [[class() alloc] init]; - @try { - TEST(@"TestPlugin's -[test:]", [test test: 1234] == 2468) + +#ifndef OF_IOS + path = [OFPlugin pathForName: @"plugin/TestPlugin"]; +#else + path = [OFPlugin pathForName: @"PlugIns/TestPlugin"]; +#endif + OTAssertNotNil(path); + + plugin = [OFPlugin pluginWithPath: path]; + OTAssertNotNil(plugin); + + class = (Class (*)(void))(uintptr_t)[plugin addressForSymbol: @"class"]; + OTAssert(class != NULL); + + @try { + test = [[class() alloc] init]; + OTAssertEqual([test test: 1234], 2468); } @finally { [test release]; } - - objc_autoreleasePoolPop(pool); } @end Index: tests/OFPropertyListTests.m ================================================================== --- tests/OFPropertyListTests.m +++ tests/OFPropertyListTests.m @@ -13,52 +13,27 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFPropertyListTests: OTTestCase +@end #define PLIST(x) \ @"" \ @"" \ @"\n" \ x @"\n" \ @"" -static OFString *const module = @"OFPropertyList"; -static OFString *const PLIST1 = PLIST(@"Hello"); -static OFString *const PLIST2 = PLIST( - @"" - @" Hello" - @" V29ybGQh" - @" 2018-03-14T12:34:56Z" - @" " - @" " - @" 12.25" - @" -10" - @""); -static OFString *const PLIST3 = PLIST( - @"" - @" array" - @" " - @" Hello" - @" V29ybGQh" - @" 2018-03-14T12:34:56Z" - @" " - @" " - @" 12.25" - @" -10" - @" " - @" foo" - @" bar" - @""); - -@implementation TestsAppDelegate (OFPLISTParser) -- (void)propertyListTests -{ - void *pool = objc_autoreleasePoolPush(); +@implementation OFPropertyListTests +- (void)testObjectByParsingPropertyList +{ OFArray *array = [OFArray arrayWithObjects: @"Hello", [OFData dataWithItems: "World!" count: 6], [OFDate dateWithTimeIntervalSince1970: 1521030896], [OFNumber numberWithBool: true], @@ -65,54 +40,75 @@ [OFNumber numberWithBool: false], [OFNumber numberWithFloat: 12.25f], [OFNumber numberWithInt: -10], nil]; - TEST(@"-[objectByParsingPropertyList:] #1", - [PLIST1.objectByParsingPropertyList isEqual: @"Hello"]) - - TEST(@"-[objectByParsingPropertyList:] #2", - [PLIST2.objectByParsingPropertyList isEqual: array]) - - TEST(@"-[objectByParsingPropertyList:] #3", - [PLIST3.objectByParsingPropertyList isEqual: - [OFDictionary dictionaryWithKeysAndObjects: + OTAssertEqualObjects([PLIST( + @"Hello") objectByParsingPropertyList], + @"Hello"); + OTAssertEqualObjects([PLIST( + @"" + @" Hello" + @" V29ybGQh" + @" 2018-03-14T12:34:56Z" + @" " + @" " + @" 12.25" + @" -10" + @"") objectByParsingPropertyList], + array); + OTAssertEqualObjects([PLIST( + @"" + @" array" + @" " + @" Hello" + @" V29ybGQh" + @" 2018-03-14T12:34:56Z" + @" " + @" " + @" 12.25" + @" -10" + @" " + @" foo" + @" bar" + @"") objectByParsingPropertyList], + ([OFDictionary dictionaryWithKeysAndObjects: @"array", array, @"foo", @"bar", - nil]]) - - EXPECT_EXCEPTION(@"Detecting unsupported version", - OFUnsupportedVersionException, - [[PLIST(@"") stringByReplacingOccurrencesOfString: @"1.0" - withString: @"1.1"] - objectByParsingPropertyList]) - - EXPECT_EXCEPTION( - @"-[objectByParsingPropertyList] detecting invalid format #1", - OFInvalidFormatException, - [PLIST(@"") objectByParsingPropertyList]) - - EXPECT_EXCEPTION( - @"-[objectByParsingPropertyList] detecting invalid format #2", - OFInvalidFormatException, - [PLIST(@"") objectByParsingPropertyList]) - - EXPECT_EXCEPTION( - @"-[objectByParsingPropertyList] detecting invalid format #3", - OFInvalidFormatException, - [PLIST(@"") objectByParsingPropertyList]) - - EXPECT_EXCEPTION( - @"-[objectByParsingPropertyList] detecting invalid format #4", - OFInvalidFormatException, + nil])); +} + +- (void)testDetectUnsupportedVersion +{ + OTAssertThrowsSpecific( + [[PLIST(@"") + stringByReplacingOccurrencesOfString: @"1.0" + withString: @"1.1"] + objectByParsingPropertyList], + OFUnsupportedVersionException); +} + +- (void)testDetectInvalidFormat +{ + OTAssertThrowsSpecific( + [PLIST(@"") objectByParsingPropertyList], + OFInvalidFormatException); + + OTAssertThrowsSpecific( + [PLIST(@"") objectByParsingPropertyList], + OFInvalidFormatException); + + OTAssertThrowsSpecific( + [PLIST(@"") objectByParsingPropertyList], + OFInvalidFormatException); + + OTAssertThrowsSpecific( [PLIST(@"") - objectByParsingPropertyList]) + objectByParsingPropertyList], + OFInvalidFormatException); - EXPECT_EXCEPTION( - @"-[objectByParsingPropertyList] detecting invalid format #5", - OFInvalidFormatException, + OTAssertThrowsSpecific( [PLIST(@"") - objectByParsingPropertyList]) - - objc_autoreleasePoolPop(pool); + objectByParsingPropertyList], + OFInvalidFormatException); } @end DELETED tests/OFRIPEMD160HashTests.m Index: tests/OFRIPEMD160HashTests.m ================================================================== --- tests/OFRIPEMD160HashTests.m +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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" - -#include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFRIPEMD160Hash"; - -const uint8_t testFileRIPEMD160[20] = - "\x46\x02\x97\xF5\x85\xDF\xB9\x21\x00\xC8\xF9\x87\xC6\xEC\x84\x0D\xCE" - "\xE6\x08\x8B"; - -@implementation TestsAppDelegate (OFRIPEMD160HashTests) -- (void)RIPEMD160HashTests -{ - void *pool = objc_autoreleasePoolPush(); - OFRIPEMD160Hash *RIPEMD160, *RIPEMD160Copy; - OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; - - TEST(@"+[hashWithAllowsSwappableMemory:]", - (RIPEMD160 = [OFRIPEMD160Hash hashWithAllowsSwappableMemory: true])) - - while (!file.atEndOfStream) { - char buffer[64]; - size_t length = [file readIntoBuffer: buffer length: 64]; - [RIPEMD160 updateWithBuffer: buffer length: length]; - } - [file close]; - - TEST(@"-[copy]", (RIPEMD160Copy = [[RIPEMD160 copy] autorelease])) - - TEST(@"-[calculate]", - R([RIPEMD160 calculate]) && R([RIPEMD160Copy calculate])) - - TEST(@"-[digest]", - memcmp(RIPEMD160.digest, testFileRIPEMD160, 20) == 0 && - memcmp(RIPEMD160Copy.digest, testFileRIPEMD160, 20) == 0) - - EXPECT_EXCEPTION(@"Detect invalid call of " - @"-[updateWithBuffer:length]", OFHashAlreadyCalculatedException, - [RIPEMD160 updateWithBuffer: "" length: 1]) - - objc_autoreleasePoolPop(pool); -} -@end DELETED tests/OFSHA1HashTests.m Index: tests/OFSHA1HashTests.m ================================================================== --- tests/OFSHA1HashTests.m +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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" - -#include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFSHA1Hash"; - -const uint8_t testFileSHA1[20] = - "\xC9\x9A\xB8\x7E\x1E\xC8\xEC\x65\xD5\xEB\xE4\x2E\x0D\xA6\x80\x96\xF5" - "\x94\xE7\x17"; - -@implementation TestsAppDelegate (SHA1HashTests) -- (void)SHA1HashTests -{ - void *pool = objc_autoreleasePoolPush(); - OFSHA1Hash *SHA1, *SHA1Copy; - OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; - - TEST(@"+[hashWithAllowsSwappableMemory:]", - (SHA1 = [OFSHA1Hash hashWithAllowsSwappableMemory: true])) - - while (!file.atEndOfStream) { - char buffer[64]; - size_t length = [file readIntoBuffer: buffer length: 64]; - [SHA1 updateWithBuffer: buffer length: length]; - } - [file close]; - - TEST(@"-[copy]", (SHA1Copy = [[SHA1 copy] autorelease])) - - TEST(@"-[calculate]", R([SHA1 calculate]) && R([SHA1Copy calculate])) - - TEST(@"-[digest]", - memcmp(SHA1.digest, testFileSHA1, 20) == 0 && - memcmp(SHA1Copy.digest, testFileSHA1, 20) == 0) - - EXPECT_EXCEPTION(@"Detect invalid call of " - @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException, - [SHA1 updateWithBuffer: "" length: 1]) - - objc_autoreleasePoolPop(pool); -} -@end DELETED tests/OFSHA224HashTests.m Index: tests/OFSHA224HashTests.m ================================================================== --- tests/OFSHA224HashTests.m +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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" - -#include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFSHA224Hash"; - -const uint8_t testFileSHA224[28] = - "\x27\x69\xD8\x04\x2D\x0F\xCA\x84\x6C\xF1\x62\x44\xBA\x0C\xBD\x46\x64" - "\x5F\x4F\x20\x02\x4D\x15\xED\x1C\x61\x1F\xF7"; - -@implementation TestsAppDelegate (SHA224HashTests) -- (void)SHA224HashTests -{ - void *pool = objc_autoreleasePoolPush(); - OFSHA224Hash *SHA224, *SHA224Copy; - OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; - - TEST(@"+[hashWithAllowsSwappableMemory:]", - (SHA224 = [OFSHA224Hash hashWithAllowsSwappableMemory: true])) - - while (!file.atEndOfStream) { - char buffer[64]; - size_t length = [file readIntoBuffer: buffer length: 64]; - [SHA224 updateWithBuffer: buffer length: length]; - } - [file close]; - - TEST(@"-[copy]", (SHA224Copy = [[SHA224 copy] autorelease])) - - TEST(@"-[calculate]", - R([SHA224 calculate]) && R([SHA224Copy calculate])) - - TEST(@"-[digest]", - memcmp(SHA224.digest, testFileSHA224, 28) == 0 && - memcmp(SHA224Copy.digest, testFileSHA224, 28) == 0) - - EXPECT_EXCEPTION(@"Detect invalid call of " - @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException, - [SHA224 updateWithBuffer: "" length: 1]) - - objc_autoreleasePoolPop(pool); -} -@end DELETED tests/OFSHA256HashTests.m Index: tests/OFSHA256HashTests.m ================================================================== --- tests/OFSHA256HashTests.m +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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" - -#include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFSHA256Hash"; - -const uint8_t testFileSHA256[32] = - "\x1A\x02\xD6\x46\xF5\xA6\xBA\xAA\xFF\x7F\xD5\x87\xBA\xC3\xF6\xC6\xB5" - "\x67\x93\x8F\x0F\x44\x90\xB8\xF5\x35\x89\xF0\x5A\x23\x7F\x69"; - -@implementation TestsAppDelegate (SHA256HashTests) -- (void)SHA256HashTests -{ - void *pool = objc_autoreleasePoolPush(); - OFSHA256Hash *SHA256, *SHA256Copy; - OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; - - TEST(@"+[hashWithAllowsSwappableMemory:]", - (SHA256 = [OFSHA256Hash hashWithAllowsSwappableMemory: true])) - - while (!file.atEndOfStream) { - char buffer[64]; - size_t length = [file readIntoBuffer: buffer length: 64]; - [SHA256 updateWithBuffer: buffer length: length]; - } - [file close]; - - TEST(@"-[copy]", (SHA256Copy = [[SHA256 copy] autorelease])) - - TEST(@"-[calculate]", - R([SHA256 calculate]) && R([SHA256Copy calculate])) - - TEST(@"-[digest]", - memcmp(SHA256.digest, testFileSHA256, 32) == 0 && - memcmp(SHA256Copy.digest, testFileSHA256, 32) == 0) - - EXPECT_EXCEPTION(@"Detect invalid call of " - @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException, - [SHA256 updateWithBuffer: "" length: 1]) - - objc_autoreleasePoolPop(pool); -} -@end DELETED tests/OFSHA384HashTests.m Index: tests/OFSHA384HashTests.m ================================================================== --- tests/OFSHA384HashTests.m +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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" - -#include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFSHA384Hash"; - -const uint8_t testFileSHA384[48] = - "\x7E\xDE\x62\xE2\x10\xA5\x1E\x18\x8A\x11\x7F\x78\xD7\xC7\x55\xB6\x43" - "\x94\x1B\xD2\x78\x5C\xCF\xF3\x8A\xB8\x98\x22\xC7\x0E\xFE\xF1\xEC\x53" - "\xE9\x1A\xB3\x51\x70\x8C\x1F\x3F\x56\x12\x44\x01\x91\x54"; - -@implementation TestsAppDelegate (SHA384HashTests) -- (void)SHA384HashTests -{ - void *pool = objc_autoreleasePoolPush(); - OFSHA384Hash *SHA384, *SHA384Copy; - OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; - - TEST(@"+[hashWithAllowsSwappableMemory:]", - (SHA384 = [OFSHA384Hash hashWithAllowsSwappableMemory: true])) - - while (!file.atEndOfStream) { - char buffer[128]; - size_t length = [file readIntoBuffer: buffer length: 128]; - [SHA384 updateWithBuffer: buffer length: length]; - } - [file close]; - - TEST(@"-[copy]", (SHA384Copy = [[SHA384 copy] autorelease])) - - TEST(@"-[calculate]", - R([SHA384 calculate]) && R([SHA384Copy calculate])) - - TEST(@"-[digest]", - memcmp(SHA384.digest, testFileSHA384, 48) == 0 && - memcmp(SHA384Copy.digest, testFileSHA384, 48) == 0) - - EXPECT_EXCEPTION(@"Detect invalid call of " - @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException, - [SHA384 updateWithBuffer: "" length: 1]) - - objc_autoreleasePoolPop(pool); -} -@end DELETED tests/OFSHA512HashTests.m Index: tests/OFSHA512HashTests.m ================================================================== --- tests/OFSHA512HashTests.m +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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" - -#include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFSHA512Hash"; - -const uint8_t testFileSHA512[64] = - "\x8F\x36\x6E\x3C\x19\x4B\xBB\xC7\x82\xAA\xCD\x7D\x55\xA2\xD3\x29\x29" - "\x97\x6A\x3F\xEB\x9B\xB2\xCB\x75\xC9\xEC\xC8\x10\x07\xD6\x07\x31\x4A" - "\xB1\x30\x97\x82\x58\xA5\x1F\x71\x42\xE6\x56\x07\x99\x57\xB2\xB8\x3B" - "\xA1\x8A\x41\x64\x33\x69\x21\x8C\x2A\x44\x6D\xF2\xA0"; - -@implementation TestsAppDelegate (SHA512HashTests) -- (void)SHA512HashTests -{ - void *pool = objc_autoreleasePoolPush(); - OFSHA512Hash *SHA512, *SHA512Copy; - OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; - - TEST(@"+[hashWithAllowsSwappableMemory:]", - (SHA512 = [OFSHA512Hash hashWithAllowsSwappableMemory: true])) - - while (!file.atEndOfStream) { - char buffer[128]; - size_t length = [file readIntoBuffer: buffer length: 128]; - [SHA512 updateWithBuffer: buffer length: length]; - } - [file close]; - - TEST(@"-[copy]", (SHA512Copy = [[SHA512 copy] autorelease])) - - TEST(@"-[calculate]", - R([SHA512 calculate]) && R([SHA512Copy calculate])) - - TEST(@"-[digest]", - memcmp(SHA512.digest, testFileSHA512, 64) == 0 && - memcmp(SHA512Copy.digest, testFileSHA512, 64) == 0) - - EXPECT_EXCEPTION(@"Detect invalid call of " - @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException, - [SHA512 updateWithBuffer: "" length: 1]) - - objc_autoreleasePoolPop(pool); -} -@end Index: tests/OFSPXSocketTests.m ================================================================== --- tests/OFSPXSocketTests.m +++ tests/OFSPXSocketTests.m @@ -14,14 +14,21 @@ */ #include "config.h" #include +#include -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFSPXSocket"; +@interface OFSPXSocketTests: OTTestCase +{ + OFSPXSocket *_sockServer; + OFSocketAddress _addrServer; +} +@end @interface SPXSocketDelegate: OFObject { @public OFSequencedPacketSocket *_expectedServerSocket; @@ -29,10 +36,167 @@ unsigned char _expectedNode[IPX_NODE_LEN]; uint32_t _expectedNetwork; uint16_t _expectedPort; bool _accepted; bool _connected; +} +@end + +@implementation OFSPXSocketTests +- (void)setUp +{ + const unsigned char zeroNode[IPX_NODE_LEN] = { 0 }; + + _sockServer = [[OFSPXSocket alloc] init]; + + @try { + _addrServer = [_sockServer bindToNetwork: 0 + node: zeroNode + port: 0]; + } @catch (OFBindSocketFailedException *e) { + switch (e.errNo) { + case EAFNOSUPPORT: + OTSkip(@"IPX unsupported"); + case ESOCKTNOSUPPORT: + OTSkip(@"SPX unsupported"); + case EADDRNOTAVAIL: + OTSkip(@"IPX not configured"); + default: + @throw e; + } + } +} + +- (void)dealloc +{ + [_sockServer release]; + + [super dealloc]; +} + +- (void)testSPXSocket +{ + OFSPXSocket *sockClient, *sockAccepted; + const OFSocketAddress *addrAccepted; + uint32_t network; + unsigned char node[IPX_NODE_LEN], node2[IPX_NODE_LEN]; + uint16_t port; + OFDictionary *networkInterfaces; + char buffer[5]; + + sockClient = [OFSPXSocket socket]; + + network = OFSocketAddressIPXNetwork(&_addrServer); + OFSocketAddressGetIPXNode(&_addrServer, node); + port = OFSocketAddressIPXPort(&_addrServer); + + [_sockServer listen]; + + /* + * Find any network interface with IPX and send to it. Any should be + * fine since we bound to 0.0. + */ + networkInterfaces = [OFSystemInfo networkInterfaces]; + for (OFString *name in networkInterfaces) { + OFNetworkInterface interface = [networkInterfaces + objectForKey: name]; + OFData *addresses = [interface + objectForKey: OFNetworkInterfaceIPXAddresses]; + + if (addresses.count == 0) + continue; + + network = OFSocketAddressIPXNetwork([addresses itemAtIndex: 0]); + OFSocketAddressGetIPXNode([addresses itemAtIndex: 0], node); + } + + [sockClient connectToNetwork: network node: node port: port]; + + sockAccepted = [_sockServer accept]; + [sockAccepted sendBuffer: "Hello" length: 5]; + + OTAssertEqual([sockClient receiveIntoBuffer: buffer length: 5], 5); + OTAssertEqual(memcmp(buffer, "Hello", 5), 0); + + addrAccepted = sockAccepted.remoteAddress; + OFSocketAddressGetIPXNode(addrAccepted, node2); + OTAssertEqual(memcmp(node, node2, IPX_NODE_LEN), 0); +} + +- (void)testAsyncSPXSocket +{ + SPXSocketDelegate *delegate = + [[[SPXSocketDelegate alloc] init] autorelease]; + uint32_t network; + unsigned char node[IPX_NODE_LEN]; + uint16_t port; + OFDictionary *networkInterfaces; + OFSPXSocket *sockClient; + + delegate->_expectedServerSocket = _sockServer; + _sockServer.delegate = delegate; + + sockClient = [OFSPXSocket socket]; + delegate->_expectedClientSocket = sockClient; + sockClient.delegate = delegate; + + [_sockServer listen]; + [_sockServer asyncAccept]; + + network = OFSocketAddressIPXNetwork(&_addrServer); + OFSocketAddressGetIPXNode(&_addrServer, node); + port = OFSocketAddressIPXPort(&_addrServer); + + /* + * Find any network interface with IPX and send to it. Any should be + * fine since we bound to 0.0. + */ + networkInterfaces = [OFSystemInfo networkInterfaces]; + for (OFString *name in networkInterfaces) { + OFNetworkInterface interface = [networkInterfaces + objectForKey: name]; + OFData *addresses = [interface + objectForKey: OFNetworkInterfaceIPXAddresses]; + + if (addresses.count == 0) + continue; + + network = OFSocketAddressIPXNetwork([addresses itemAtIndex: 0]); + OFSocketAddressGetIPXNode([addresses itemAtIndex: 0], node); + } + + delegate->_expectedNetwork = network = + OFSocketAddressIPXNetwork(&_addrServer); + OFSocketAddressGetIPXNode(&_addrServer, node); + memcpy(delegate->_expectedNode, node, IPX_NODE_LEN); + delegate->_expectedPort = port = OFSocketAddressIPXPort(&_addrServer); + + @try { + [sockClient asyncConnectToNetwork: network + node: node + port: port]; + + [[OFRunLoop mainRunLoop] runUntilDate: + [OFDate dateWithTimeIntervalSinceNow: 2]]; + + OTAssertTrue(delegate->_accepted); + OTAssertTrue(delegate->_connected); + } @catch (OFObserveKernelEventsFailedException *e) { + /* + * Make sure it doesn't stay in the run loop and throws again + * next time we run the run loop. + */ + [sockClient cancelAsyncRequests]; + [_sockServer cancelAsyncRequests]; + + switch (e.errNo) { + case ENOTSOCK: + OTSkip(@"select() not supported for SPX"); + default: + @throw e; + } + } } @end @implementation SPXSocketDelegate - (bool)socket: (OFSequencedPacketSocket *)sock @@ -64,153 +228,6 @@ port == _expectedPort && exception == nil); if (_accepted && _connected) [[OFRunLoop mainRunLoop] stop]; } -@end - -@implementation TestsAppDelegate (OFSPXSocketTests) -- (void)SPXSocketTests -{ - const unsigned char zeroNode[IPX_NODE_LEN] = { 0 }; - void *pool = objc_autoreleasePoolPush(); - OFSPXSocket *sockClient, *sockServer = nil, *sockAccepted; - OFSocketAddress address1; - const OFSocketAddress *address2; - uint32_t network; - unsigned char node[IPX_NODE_LEN], node2[IPX_NODE_LEN]; - uint16_t port; - OFDictionary *networkInterfaces; - char buffer[5]; - SPXSocketDelegate *delegate; - - TEST(@"+[socket]", (sockClient = [OFSPXSocket socket]) && - (sockServer = [OFSPXSocket socket])) - - @try { - TEST(@"-[bindToNetwork:node:port:]", - R(address1 = [sockServer bindToNetwork: 0 - node: zeroNode - port: 0])) - } @catch (OFBindSocketFailedException *e) { - switch (e.errNo) { - case EAFNOSUPPORT: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFSPXSocket] -[bindToNetwork:node:port:]: " - @"IPX unsupported, skipping tests"]; - break; - case ESOCKTNOSUPPORT: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFSPXSocket] -[bindToNetwork:node:port:]: " - @"SPX unsupported, skipping tests"]; - break; - case EADDRNOTAVAIL: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFSPXSocket] -[bindToNetwork:node:port:]: " - @"IPX not configured, skipping tests"]; - break; - default: - @throw e; - } - - objc_autoreleasePoolPop(pool); - return; - } - - network = OFSocketAddressIPXNetwork(&address1); - OFSocketAddressGetIPXNode(&address1, node); - port = OFSocketAddressIPXPort(&address1); - - TEST(@"-[listen]", R([sockServer listen])) - - /* - * Find any network interface with IPX and send to it. Any should be - * fine since we bound to 0.0. - */ - networkInterfaces = [OFSystemInfo networkInterfaces]; - for (OFString *name in networkInterfaces) { - OFNetworkInterface interface = [networkInterfaces - objectForKey: name]; - OFData *addresses = [interface - objectForKey: OFNetworkInterfaceIPXAddresses]; - - if (addresses.count == 0) - continue; - - network = OFSocketAddressIPXNetwork([addresses itemAtIndex: 0]); - OFSocketAddressGetIPXNode([addresses itemAtIndex: 0], node); - } - - TEST(@"-[connectToNetwork:node:port:]", - R([sockClient connectToNetwork: network node: node port: port])) - - TEST(@"-[accept]", (sockAccepted = [sockServer accept])) - - TEST(@"-[sendBuffer:length:]", - R([sockAccepted sendBuffer: "Hello" length: 5])) - - TEST(@"-[receiveIntoBuffer:length:]", - [sockClient receiveIntoBuffer: buffer length: 5] == 5 && - memcmp(buffer, "Hello", 5) == 0) - - TEST(@"-[remoteAddress]", - (address2 = sockAccepted.remoteAddress) && - R(OFSocketAddressGetIPXNode(address2, node2)) && - memcmp(node, node2, IPX_NODE_LEN) == 0) - - delegate = [[[SPXSocketDelegate alloc] init] autorelease]; - - sockServer = [OFSPXSocket socket]; - delegate->_expectedServerSocket = sockServer; - sockServer.delegate = delegate; - - sockClient = [OFSPXSocket socket]; - delegate->_expectedClientSocket = sockClient; - sockClient.delegate = delegate; - - address1 = [sockServer bindToNetwork: 0 node: zeroNode port: 0]; - [sockServer listen]; - [sockServer asyncAccept]; - - delegate->_expectedNetwork = network = - OFSocketAddressIPXNetwork(&address1); - OFSocketAddressGetIPXNode(&address1, node); - memcpy(delegate->_expectedNode, node, IPX_NODE_LEN); - delegate->_expectedPort = port = OFSocketAddressIPXPort(&address1); - - @try { - [sockClient asyncConnectToNetwork: network - node: node - port: port]; - - [[OFRunLoop mainRunLoop] runUntilDate: - [OFDate dateWithTimeIntervalSinceNow: 2]]; - - TEST(@"-[asyncAccept] & -[asyncConnectToNetwork:node:port:]", - delegate->_accepted && delegate->_connected) - } @catch (OFObserveKernelEventsFailedException *e) { - /* - * Make sure it doesn't stay in the run loop and throws again - * next time we run the run loop. - */ - [sockClient cancelAsyncRequests]; - [sockServer cancelAsyncRequests]; - - switch (e.errNo) { - case ENOTSOCK: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFSPXSocket] -[asyncAccept] & " - @"-[asyncConnectToNetwork:node:port:]: select() " - @"not supported for SPX, skipping test"]; - break; - default: - @throw e; - } - } - - objc_autoreleasePoolPop(pool); -} @end Index: tests/OFSPXStreamSocketTests.m ================================================================== --- tests/OFSPXStreamSocketTests.m +++ tests/OFSPXStreamSocketTests.m @@ -14,14 +14,21 @@ */ #include "config.h" #include +#include -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFSPXStreamSocket"; +@interface OFSPXStreamSocketTests: OTTestCase +{ + OFSPXStreamSocket *_sockServer; + OFSocketAddress _addrServer; +} +@end @interface SPXStreamSocketDelegate: OFObject { @public OFStreamSocket *_expectedServerSocket; @@ -29,10 +36,170 @@ uint32_t _expectedNetwork; unsigned char _expectedNode[IPX_NODE_LEN]; uint16_t _expectedPort; bool _accepted; bool _connected; +} +@end + +@implementation OFSPXStreamSocketTests +- (void)setUp +{ + const unsigned char zeroNode[IPX_NODE_LEN] = { 0 }; + + _sockServer = [[OFSPXStreamSocket alloc] init]; + + @try { + _addrServer = [_sockServer bindToNetwork: 0 + node: zeroNode + port: 0]; + } @catch (OFBindSocketFailedException *e) { + switch (e.errNo) { + case EAFNOSUPPORT: + OTSkip(@"IPX unsupported"); + case ESOCKTNOSUPPORT: + case EPROTONOSUPPORT: + OTSkip(@"SPX unsupported"); + case EADDRNOTAVAIL: + OTSkip(@"IPX not configured"); + default: + @throw e; + } + } +} + +- (void)dealloc +{ + [_sockServer release]; + + [super dealloc]; +} + +- (void)testSPXStreamSocket +{ + OFSPXStreamSocket *sockClient, *sockAccepted; + const OFSocketAddress *addrAccepted; + uint32_t network; + unsigned char node[IPX_NODE_LEN], node2[IPX_NODE_LEN]; + uint16_t port; + OFDictionary *networkInterfaces; + char buffer[5]; + + sockClient = [OFSPXStreamSocket socket]; + + network = OFSocketAddressIPXNetwork(&_addrServer); + OFSocketAddressGetIPXNode(&_addrServer, node); + port = OFSocketAddressIPXPort(&_addrServer); + + [_sockServer listen]; + + /* + * Find any network interface with IPX and send to it. Any should be + * fine since we bound to 0.0. + */ + networkInterfaces = [OFSystemInfo networkInterfaces]; + for (OFString *name in networkInterfaces) { + OFNetworkInterface interface = [networkInterfaces + objectForKey: name]; + OFData *addresses = [interface + objectForKey: OFNetworkInterfaceIPXAddresses]; + + if (addresses.count == 0) + continue; + + network = OFSocketAddressIPXNetwork([addresses itemAtIndex: 0]); + OFSocketAddressGetIPXNode([addresses itemAtIndex: 0], node); + } + + [sockClient connectToNetwork: network node: node port: port]; + + sockAccepted = [_sockServer accept]; + [sockAccepted writeBuffer: "Hello" length: 5]; + + /* Test reassembly (this would not work with OFSPXSocket) */ + OTAssertEqual([sockClient readIntoBuffer: buffer length: 2], 2); + OTAssertEqual([sockClient readIntoBuffer: buffer + 2 length: 3], 3); + OTAssertEqual(memcmp(buffer, "Hello", 5), 0); + + addrAccepted = sockAccepted.remoteAddress; + OFSocketAddressGetIPXNode(addrAccepted, node2); + OTAssertEqual(memcmp(node, node2, IPX_NODE_LEN), 0); +} + +- (void)testAsyncSPXStreamSocket +{ + SPXStreamSocketDelegate *delegate = + [[[SPXStreamSocketDelegate alloc] init] autorelease]; + uint32_t network; + unsigned char node[IPX_NODE_LEN]; + uint16_t port; + OFDictionary *networkInterfaces; + OFSPXStreamSocket *sockClient; + + delegate->_expectedServerSocket = _sockServer; + _sockServer.delegate = delegate; + + sockClient = [OFSPXStreamSocket socket]; + delegate->_expectedClientSocket = sockClient; + sockClient.delegate = delegate; + + [_sockServer listen]; + [_sockServer asyncAccept]; + + network = OFSocketAddressIPXNetwork(&_addrServer); + OFSocketAddressGetIPXNode(&_addrServer, node); + port = OFSocketAddressIPXPort(&_addrServer); + + /* + * Find any network interface with IPX and send to it. Any should be + * fine since we bound to 0.0. + */ + networkInterfaces = [OFSystemInfo networkInterfaces]; + for (OFString *name in networkInterfaces) { + OFNetworkInterface interface = [networkInterfaces + objectForKey: name]; + OFData *addresses = [interface + objectForKey: OFNetworkInterfaceIPXAddresses]; + + if (addresses.count == 0) + continue; + + network = OFSocketAddressIPXNetwork([addresses itemAtIndex: 0]); + OFSocketAddressGetIPXNode([addresses itemAtIndex: 0], node); + } + + delegate->_expectedNetwork = network = + OFSocketAddressIPXNetwork(&_addrServer); + OFSocketAddressGetIPXNode(&_addrServer, node); + memcpy(delegate->_expectedNode, node, IPX_NODE_LEN); + delegate->_expectedPort = port = OFSocketAddressIPXPort(&_addrServer); + + @try { + [sockClient asyncConnectToNetwork: network + node: node + port: port]; + + [[OFRunLoop mainRunLoop] runUntilDate: + [OFDate dateWithTimeIntervalSinceNow: 2]]; + + OTAssertTrue(delegate->_accepted); + OTAssertTrue(delegate->_connected); + } @catch (OFObserveKernelEventsFailedException *e) { + /* + * Make sure it doesn't stay in the run loop and throws again + * next time we run the run loop. + */ + [sockClient cancelAsyncRequests]; + [_sockServer cancelAsyncRequests]; + + switch (e.errNo) { + case ENOTSOCK: + OTSkip(@"select() not supported for SPX"); + default: + @throw e; + } + } } @end @implementation SPXStreamSocketDelegate - (bool)socket: (OFStreamSocket *)sock @@ -64,157 +231,6 @@ port == _expectedPort && exception == nil); if (_accepted && _connected) [[OFRunLoop mainRunLoop] stop]; } -@end - -@implementation TestsAppDelegate (OFSPXStreamSocketTests) -- (void)SPXStreamSocketTests -{ - const unsigned char zeroNode[IPX_NODE_LEN] = { 0 }; - void *pool = objc_autoreleasePoolPush(); - OFSPXStreamSocket *sockClient, *sockServer = nil, *sockAccepted; - OFSocketAddress address1; - const OFSocketAddress *address2; - uint32_t network; - unsigned char node[IPX_NODE_LEN], node2[IPX_NODE_LEN]; - uint16_t port; - OFDictionary *networkInterfaces; - char buffer[5]; - SPXStreamSocketDelegate *delegate; - - TEST(@"+[socket]", (sockClient = [OFSPXStreamSocket socket]) && - (sockServer = [OFSPXStreamSocket socket])) - - @try { - TEST(@"-[bindToNetwork:node:port:]", - R(address1 = [sockServer bindToNetwork: 0 - node: zeroNode - port: 0])) - } @catch (OFBindSocketFailedException *e) { - switch (e.errNo) { - case EAFNOSUPPORT: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFSPXStreamSocket] -[bindToNetwork:node:" - @"port:]: IPX unsupported, skipping tests"]; - break; - case ESOCKTNOSUPPORT: - case EPROTONOSUPPORT: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFSPXStreamSocket] -[bindToNetwork:node:" - @"port:]: SPX unsupported, skipping tests"]; - break; - case EADDRNOTAVAIL: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFSPXStreamSocket] -[bindToNetwork:node:" - @"port:]: IPX not configured, skipping tests"]; - break; - default: - @throw e; - } - - objc_autoreleasePoolPop(pool); - return; - } - - network = OFSocketAddressIPXNetwork(&address1); - OFSocketAddressGetIPXNode(&address1, node); - port = OFSocketAddressIPXPort(&address1); - - TEST(@"-[listen]", R([sockServer listen])) - - /* - * Find any network interface with IPX and send to it. Any should be - * fine since we bound to 0.0. - */ - networkInterfaces = [OFSystemInfo networkInterfaces]; - for (OFString *name in networkInterfaces) { - OFNetworkInterface interface = [networkInterfaces - objectForKey: name]; - OFData *addresses = [interface - objectForKey: OFNetworkInterfaceIPXAddresses]; - - if (addresses.count == 0) - continue; - - network = OFSocketAddressIPXNetwork([addresses itemAtIndex: 0]); - OFSocketAddressGetIPXNode([addresses itemAtIndex: 0], node); - } - - TEST(@"-[connectToNetwork:node:port:]", - R([sockClient connectToNetwork: network node: node port: port])) - - TEST(@"-[accept]", (sockAccepted = [sockServer accept])) - - /* Test reassembly (this would not work with OFSPXSocket) */ - TEST(@"-[writeBuffer:length:]", - R([sockAccepted writeBuffer: "Hello" length: 5])) - - TEST(@"-[readIntoBuffer:length:]", - [sockClient readIntoBuffer: buffer length: 2] == 2 && - memcmp(buffer, "He", 2) == 0 && - [sockClient readIntoBuffer: buffer length: 3] == 3 && - memcmp(buffer, "llo", 3) == 0) - - TEST(@"-[remoteAddress]", - (address2 = sockAccepted.remoteAddress) && - R(OFSocketAddressGetIPXNode(address2, node2)) && - memcmp(node, node2, IPX_NODE_LEN) == 0) - - delegate = [[[SPXStreamSocketDelegate alloc] init] autorelease]; - - sockServer = [OFSPXStreamSocket socket]; - delegate->_expectedServerSocket = sockServer; - sockServer.delegate = delegate; - - sockClient = [OFSPXStreamSocket socket]; - delegate->_expectedClientSocket = sockClient; - sockClient.delegate = delegate; - - address1 = [sockServer bindToNetwork: 0 node: zeroNode port: 0]; - [sockServer listen]; - [sockServer asyncAccept]; - - delegate->_expectedNetwork = network = - OFSocketAddressIPXNetwork(&address1); - OFSocketAddressGetIPXNode(&address1, node); - memcpy(delegate->_expectedNode, node, IPX_NODE_LEN); - delegate->_expectedPort = port = OFSocketAddressIPXPort(&address1); - - @try { - [sockClient asyncConnectToNetwork: network - node: node - port: port]; - - [[OFRunLoop mainRunLoop] runUntilDate: - [OFDate dateWithTimeIntervalSinceNow: 2]]; - - TEST(@"-[asyncAccept] & -[asyncConnectToNetwork:node:port:]", - delegate->_accepted && delegate->_connected) - } @catch (OFObserveKernelEventsFailedException *e) { - /* - * Make sure it doesn't stay in the run loop and throws again - * next time we run the run loop. - */ - [sockClient cancelAsyncRequests]; - [sockServer cancelAsyncRequests]; - - switch (e.errNo) { - case ENOTSOCK: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFSPXStreamSocket] -[asyncAccept] & " - @"-[asyncConnectToNetwork:node:port:]: select() " - @"not supported for SPX, skipping test"]; - break; - default: - @throw e; - } - } - - objc_autoreleasePoolPop(pool); -} @end Index: tests/OFScryptTests.m ================================================================== --- tests/OFScryptTests.m +++ tests/OFScryptTests.m @@ -15,13 +15,16 @@ #include "config.h" #include -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFScrypt"; +@interface OFScryptTests: OTTestCase +@end + /* Test vectors form RFC 7914 */ static const unsigned char salsa20Input[64] = { 0x7E, 0x87, 0x9A, 0x21, 0x4F, 0x3E, 0xC9, 0x86, 0x7C, 0xA9, 0x40, 0xE6, 0x41, 0x71, 0x8F, 0x26, 0xBA, 0xEE, 0x55, 0x5B, 0x8C, 0x61, 0xC1, 0xB5, 0x0D, 0xF8, 0x46, 0x11, 0x6D, 0xCD, 0x3B, 0x1D, 0xEE, 0x24, 0xF3, 0x19, @@ -100,20 +103,26 @@ 0xDF, 0xDF, 0xFA, 0x3F, 0xED, 0xE2, 0x14, 0x42, 0xFC, 0xD0, 0x06, 0x9D, 0xED, 0x09, 0x48, 0xF8, 0x32, 0x6A, 0x75, 0x3A, 0x0F, 0xC8, 0x1F, 0x17, 0xE8, 0xD3, 0xE0, 0xFB, 0x2E, 0x0D, 0x36, 0x28, 0xCF, 0x35, 0xE2, 0x0C, 0x38, 0xD1, 0x89, 0x06 }; +/* Nintendo DS does not have enough RAM for the second test vector. */ +#ifndef OF_NINTENDO_DS static const unsigned char testVector2[64] = { 0xFD, 0xBA, 0xBE, 0x1C, 0x9D, 0x34, 0x72, 0x00, 0x78, 0x56, 0xE7, 0x19, 0x0D, 0x01, 0xE9, 0xFE, 0x7C, 0x6A, 0xD7, 0xCB, 0xC8, 0x23, 0x78, 0x30, 0xE7, 0x73, 0x76, 0x63, 0x4B, 0x37, 0x31, 0x62, 0x2E, 0xAF, 0x30, 0xD9, 0x2E, 0x22, 0xA3, 0x88, 0x6F, 0xF1, 0x09, 0x27, 0x9D, 0x98, 0x30, 0xDA, 0xC7, 0x27, 0xAF, 0xB9, 0x4A, 0x83, 0xEE, 0x6D, 0x83, 0x60, 0xCB, 0xDF, 0xA2, 0xCC, 0x06, 0x40 }; -/* The third test vector is too expensive for m68k. */ -#ifndef OF_M68K +#endif +/* + * The third test vector is too expensive for m68k. + * Nintendo DS does not have enough RAM for the third test vector. + */ +#if !defined(OF_M68K) && !defined(OF_NINTENDO_DS) static const unsigned char testVector3[64] = { 0x70, 0x23, 0xBD, 0xCB, 0x3A, 0xFD, 0x73, 0x48, 0x46, 0x1C, 0x06, 0xCD, 0x81, 0xFD, 0x38, 0xEB, 0xFD, 0xA8, 0xFB, 0xBA, 0x90, 0x4F, 0x8E, 0x3E, 0xA9, 0xB5, 0x43, 0xF6, 0x54, 0x5D, 0xA1, 0xF2, 0xD5, 0x43, 0x29, 0x55, 0x61, 0x3F, 0x0F, 0xCF, 0x62, 0xD4, 0x97, 0x05, 0x24, 0x2A, 0x9A, 0xF9, @@ -131,35 +140,42 @@ 0x37, 0x30, 0x40, 0x49, 0xE8, 0xA9, 0x52, 0xFB, 0xCB, 0xF4, 0x5C, 0x6F, 0xA7, 0x7A, 0x41, 0xA4 }; #endif -@implementation TestsAppDelegate (OFScryptTests) -- (void)scryptTests +@implementation OFScryptTests +- (void)testSalsa20_8Core { - void *pool = objc_autoreleasePoolPush(); uint32_t salsa20Buffer[16]; + + memcpy(salsa20Buffer, salsa20Input, 64); + OFSalsa20_8Core(salsa20Buffer); + OTAssertEqual(memcmp(salsa20Buffer, salsa20Output, 64), 0); +} + +- (void)testBlockMix +{ uint32_t blockMixBuffer[32]; + + OFScryptBlockMix(blockMixBuffer, blockMixInput.u32, 1); + OTAssertEqual(memcmp(blockMixBuffer, blockMixOutput, 128), 0); +} + +- (void)testROMix +{ uint32_t ROMixBuffer[32], ROMixTmp[17 * 32]; + + memcpy(ROMixBuffer, ROMixInput, 128); + OFScryptROMix(ROMixBuffer, 1, 16, ROMixTmp); + OTAssertEqual(memcmp(ROMixBuffer, ROMixOutput, 128), 0); +} + +- (void)testRFC7941TestVector1 +{ unsigned char output[64]; - TEST(@"Salsa20/8 Core", - R(memcpy(salsa20Buffer, salsa20Input, 64)) && - R(OFSalsa20_8Core(salsa20Buffer)) && - memcmp(salsa20Buffer, salsa20Output, 64) == 0) - - TEST(@"Block mix", - R(OFScryptBlockMix(blockMixBuffer, blockMixInput.u32, 1)) && - memcmp(blockMixBuffer, blockMixOutput, 128) == 0) - - TEST(@"ROMix", - R(memcpy(ROMixBuffer, ROMixInput, 128)) && - R(OFScryptROMix(ROMixBuffer, 1, 16, ROMixTmp)) && - memcmp(ROMixBuffer, ROMixOutput, 128) == 0) - - TEST(@"scrypt test vector #1", - R(OFScrypt((OFScryptParameters){ + OFScrypt((OFScryptParameters){ .blockSize = 1, .costFactor = 16, .parallelization = 1, .salt = (unsigned char *)"", .saltLength = 0, @@ -166,14 +182,22 @@ .password = "", .passwordLength = 0, .key = output, .keyLength = 64, .allowsSwappableMemory = true - })) && memcmp(output, testVector1, 64) == 0) + }); + + OTAssertEqual(memcmp(output, testVector1, 64), 0); +} + +/* Nintendo DS does not have enough RAM for the second test vector. */ +#ifndef OF_NINTENDO_DS +- (void)testRFC7941TestVector2 +{ + unsigned char output[64]; - TEST(@"scrypt test vector #2", - R(OFScrypt((OFScryptParameters){ + OFScrypt((OFScryptParameters){ .blockSize = 8, .costFactor = 1024, .parallelization = 16, .salt = (unsigned char *)"NaCl", .saltLength = 4, @@ -180,16 +204,26 @@ .password = "password", .passwordLength = 8, .key = output, .keyLength = 64, .allowsSwappableMemory = true - })) && memcmp(output, testVector2, 64) == 0) + }); + + OTAssertEqual(memcmp(output, testVector2, 64), 0); +} +#endif + +/* + * The third test vector is too expensive for m68k. + * Nintendo DS does not have enough RAM for the third test vector. + */ +#if !defined(OF_M68K) && !defined(OF_NINTENDO_DS) +- (void)testRFC7941TestVector3 +{ + unsigned char output[64]; - /* The third test vector is too expensive for m68k. */ -#ifndef OF_M68K - TEST(@"scrypt test vector #3", - R(OFScrypt((OFScryptParameters){ + OFScrypt((OFScryptParameters){ .blockSize = 8, .costFactor = 16384, .parallelization = 1, .salt = (unsigned char *)"SodiumChloride", .saltLength = 14, @@ -196,17 +230,23 @@ .password = "pleaseletmein", .passwordLength = 13, .key = output, .keyLength = 64, .allowsSwappableMemory = true - })) && memcmp(output, testVector3, 64) == 0) + }); + + OTAssertEqual(memcmp(output, testVector3, 64), 0); +} #endif - /* The forth test vector is too expensive to include it in the tests. */ +/* The forth test vector is too expensive to include it in the tests. */ #if 0 - TEST(@"scrypt test vector #4", - R(OFScrypt((OFScryptParameters){ +- (void)testRFC7941TestVector4 +{ + unsigned char output[64]; + + OFScrypt((OFScryptParameters){ .blockSize = 8, .costFactor = 1048576, .parallelization = 1, .salt = (unsigned char *)"SodiumChloride", .saltLength = 14, @@ -213,11 +253,11 @@ .password = "pleaseletmein", .passwordLength = 13, .key = output, .keyLength = 64, .allowsSwappableMemory = true - })) && memcmp(output, testVector4, 64) == 0) -#endif + }); - objc_autoreleasePoolPop(pool); + OTAssertEqual(memcmp(output, testVector4, 64), 0); } +#endif @end ADDED tests/OFSetTests.h Index: tests/OFSetTests.h ================================================================== --- /dev/null +++ tests/OFSetTests.h @@ -0,0 +1,25 @@ +/* + * 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 "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFSetTests: OTTestCase +{ + OFSet *_set; +} + +@property (readonly, nonatomic) Class setClass; +@end Index: tests/OFSetTests.m ================================================================== --- tests/OFSetTests.m +++ tests/OFSetTests.m @@ -13,81 +13,189 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -#import "OFSet.h" -#import "OFConcreteSet.h" -#import "OFConcreteMutableSet.h" - -static OFString *module; - -@interface SimpleSet: OFSet -{ - OFMutableSet *_set; -} -@end - -@interface SimpleMutableSet: OFMutableSet -{ - OFMutableSet *_set; - unsigned long _mutations; -} -@end - -@implementation SimpleSet -- (instancetype)init -{ - self = [super init]; - - @try { - _set = [[OFMutableSet alloc] init]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (instancetype)initWithSet: (OFSet *)set -{ - self = [super init]; - - @try { - _set = [[OFMutableSet alloc] initWithSet: set]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (instancetype)initWithArray: (OFArray *)array -{ - self = [super init]; - - @try { - _set = [[OFMutableSet alloc] initWithArray: array]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments -{ - self = [super init]; - - @try { - _set = [[OFMutableSet alloc] initWithObject: firstObject - arguments: arguments]; +#import "OFSetTests.h" + +@interface CustomSet: OFSet +{ + OFSet *_set; +} +@end + +@implementation OFSetTests +- (Class)setClass +{ + return [CustomSet class]; +} + +- (void)setUp +{ + [super setUp]; + + _set = [[OFSet alloc] initWithObjects: @"foo", @"bar", @"baz", nil]; +} + +- (void)dealloc +{ + [_set release]; + + [super dealloc]; +} + +- (void)testSetWithArray +{ + OTAssertEqualObjects([self.setClass setWithArray: + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", @"foo", nil])], + _set); +} + +- (void)testIsEqual +{ + OTAssertEqualObjects(_set, + ([OFSet setWithObjects: @"foo", @"bar", @"baz", nil])); +} + +- (void)testHash +{ + OTAssertEqual(_set.hash, + [([OFSet setWithObjects: @"foo", @"bar", @"baz", nil]) hash]); +} + +- (void)testDescription +{ + OFString *description = _set.description; + + OTAssert( + [description isEqual: @"{(\n\tfoo,\n\tbar,\n\tbaz\n)}"] || + [description isEqual: @"{(\n\tfoo,\n\tbaz,\n\tbar\n)}"] || + [description isEqual: @"{(\n\tbar,\n\tfoo,\n\tbaz\n)}"] || + [description isEqual: @"{(\n\tbar,\n\tbaz,\n\tfoo\n)}"] || + [description isEqual: @"{(\n\tbaz,\n\tfoo,\n\tbar\n)}"] || + [description isEqual: @"{(\n\tbaz,\n\tbar,\n\tfoo\n)}"]); +} + +- (void)testCopy +{ + OTAssertEqualObjects([[_set copy] autorelease], _set); +} + +- (void)testIsSubsetOfSet +{ + OTAssertTrue([([OFSet setWithObjects: @"foo", nil]) + isSubsetOfSet: _set]); + OTAssertFalse([([OFSet setWithObjects: @"foo", @"Foo", nil]) + isSubsetOfSet: _set]); +} + +- (void)testIntersectsSet +{ + OTAssertTrue([([OFSet setWithObjects: @"foo", @"Foo", nil]) + intersectsSet: _set]); + OTAssertFalse([([OFSet setWithObjects: @"Foo", nil]) + intersectsSet: _set]); +} + +- (void)testEnumerator +{ + OFEnumerator *enumerator = [_set objectEnumerator]; + bool seenFoo = false, seenBar = false, seenBaz = false; + OFString *object; + + while ((object = [enumerator nextObject]) != nil) { + if ([object isEqual: @"foo"]) { + OTAssertFalse(seenFoo); + seenFoo = true; + } else if ([object isEqual: @"bar"]) { + OTAssertFalse(seenBar); + seenBar = true; + } else if ([object isEqual: @"baz"]) { + OTAssertFalse(seenBaz); + seenBaz = true; + } else + OTAssert(false, @"Unexpected object seen: %@", object); + } + + OTAssert(seenFoo && seenBar && seenBaz); +} + +- (void)testFastEnumeration +{ + bool seenFoo = false, seenBar = false, seenBaz = false; + + for (OFString *object in _set) { + if ([object isEqual: @"foo"]) { + OTAssertFalse(seenFoo); + seenFoo = true; + } else if ([object isEqual: @"bar"]) { + OTAssertFalse(seenBar); + seenBar = true; + } else if ([object isEqual: @"baz"]) { + OTAssertFalse(seenBaz); + seenBaz = true; + } else + OTAssert(false, @"Unexpected object seen: %@", object); + } + + OTAssert(seenFoo && seenBar && seenBaz); +} + +#ifdef OF_HAVE_BLOCKS +- (void)testEnumerateObjectsUsingBlock +{ + __block bool seenFoo = false, seenBar = false, seenBaz = false; + + [_set enumerateObjectsUsingBlock: ^ (id object, bool *stop) { + if ([object isEqual: @"foo"]) { + OTAssertFalse(seenFoo); + seenFoo = true; + } else if ([object isEqual: @"bar"]) { + OTAssertFalse(seenBar); + seenBar = true; + } else if ([object isEqual: @"baz"]) { + OTAssertFalse(seenBaz); + seenBaz = true; + } else + OTAssert(false, @"Unexpected object seen: %@", object); + }]; + + OTAssert(seenFoo && seenBar && seenBaz); +} + +- (void)testFilteredSetUsingBlock +{ + OFSet *filteredSet = [_set filteredSetUsingBlock: ^ (id object) { + return [object hasPrefix: @"ba"]; + }]; + + OTAssertEqualObjects(filteredSet, + ([OFSet setWithObjects: @"bar", @"baz", nil])); +} +#endif + +- (void)testValueForKey +{ + OFSet *set = [[self.setClass setWithObjects: + @"a", @"ab", @"abc", @"b", nil] valueForKey: @"length"]; + + OTAssertEqualObjects(set, ([OFSet setWithObjects: + [OFNumber numberWithInt: 1], [OFNumber numberWithInt: 2], + [OFNumber numberWithInt: 3], nil])); + + OTAssertEqualObjects([set valueForKey: @"@count"], + [OFNumber numberWithInt: 3]); +} +@end + +@implementation CustomSet +- (instancetype)initWithObjects: (id const *)objects count: (size_t)count +{ + self = [super init]; + + @try { + _set = [[OFSet alloc] initWithObjects: objects count: count]; } @catch (id e) { [self release]; @throw e; } @@ -113,183 +221,6 @@ - (OFEnumerator *)objectEnumerator { return [_set objectEnumerator]; } -@end - -@implementation SimpleMutableSet -+ (void)initialize -{ - if (self == [SimpleMutableSet class]) - [self inheritMethodsFromClass: [SimpleSet class]]; -} - -- (void)addObject: (id)object -{ - bool existed = [self containsObject: object]; - - [_set addObject: object]; - - if (existed) - _mutations++; -} - -- (void)removeObject: (id)object -{ - bool existed = [self containsObject: object]; - - [_set removeObject: object]; - - if (existed) - _mutations++; -} - -- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state - objects: (id *)objects - count: (int)count -{ - int ret = [_set countByEnumeratingWithState: state - objects: objects - count: count]; - - state->mutationsPtr = &_mutations; - - return ret; -} -@end - -@implementation TestsAppDelegate (OFSetTests) -- (void)setTestsWithClass: (Class)setClass mutableClass: (Class)mutableSetClass -{ - void *pool = objc_autoreleasePoolPush(); - OFSet *set1, *set2; - OFMutableSet *mutableSet; - bool ok; - size_t i; - - TEST(@"+[setWithArray:]", - (set1 = [setClass setWithArray: [OFArray arrayWithObjects: @"foo", - @"bar", @"baz", @"foo", @"x", nil]])) - - TEST(@"+[setWithObjects:]", - (set2 = [setClass setWithObjects: @"foo", @"bar", @"baz", @"bar", - @"x", nil])) - - TEST(@"-[isEqual:]", [set1 isEqual: set2]) - - TEST(@"-[hash]", set1.hash == set2.hash) - - TEST(@"-[description]", - [set1.description - isEqual: @"{(\n\tx,\n\tbar,\n\tfoo,\n\tbaz\n)}"] && - [set1.description isEqual: set2.description]) - - TEST(@"-[copy]", [set1 isEqual: [[set1 copy] autorelease]]) - - TEST(@"-[mutableCopy]", - [set1 isEqual: [[set1 mutableCopy] autorelease]]); - - mutableSet = [mutableSetClass setWithSet: set1]; - - TEST(@"-[addObject:]", - R([mutableSet addObject: @"baz"]) && [mutableSet isEqual: set2] && - R([mutableSet addObject: @"y"]) && [mutableSet isEqual: - [setClass setWithObjects: @"foo", @"bar", @"baz", @"x", @"y", nil]]) - - TEST(@"-[removeObject:]", - R([mutableSet removeObject: @"y"]) && [mutableSet isEqual: set1]) - - TEST(@"-[isSubsetOfSet:]", - R([mutableSet removeObject: @"foo"]) && - [mutableSet isSubsetOfSet: set1] && - ![set1 isSubsetOfSet: mutableSet]); - - TEST(@"-[intersectsSet:]", - [(set2 = [setClass setWithObjects: @"x", nil]) - intersectsSet: set1] && [set1 intersectsSet: set2] && - ![[setClass setWithObjects: @"1", nil] intersectsSet: set1]); - - TEST(@"-[minusSet:]", - R([mutableSet minusSet: [setClass setWithObjects: @"x", nil]]) && - [mutableSet isEqual: [setClass setWithObjects: - @"baz", @"bar", nil]]) - - TEST(@"-[intersectSet:]", - R([mutableSet intersectSet: [setClass setWithObjects: - @"baz", nil]]) && [mutableSet isEqual: [setClass setWithObjects: - @"baz", nil]]) - - TEST(@"-[unionSet:]", - R([mutableSet unionSet: [setClass setWithObjects: - @"x", @"bar", nil]]) && [mutableSet isEqual: - [setClass setWithObjects: @"baz", @"bar", @"x", nil]]) - - TEST(@"-[removeAllObjects]", - R([mutableSet removeAllObjects]) && - [mutableSet isEqual: [setClass set]]) - - ok = true; - i = 0; - - for (OFString *s in set1) { - switch (i) { - case 0: - if (![s isEqual: @"x"]) - ok = false; - break; - case 1: - if (![s isEqual: @"bar"]) - ok = false; - break; - case 2: - if (![s isEqual: @"foo"]) - ok = false; - break; - case 3: - if (![s isEqual: @"baz"]) - ok = false; - break; - } - - i++; - } - - if (i != 4) - ok = false; - - TEST(@"Fast enumeration", ok) - - ok = false; - [mutableSet addObject: @"foo"]; - [mutableSet addObject: @"bar"]; - @try { - for (OFString *s in mutableSet) - [mutableSet removeObject: s]; - } @catch (OFEnumerationMutationException *e) { - ok = true; - } - - TEST(@"Detection of mutation during Fast Enumeration", ok); - - TEST(@"-[valueForKey:]", - [(set1 = [[setClass setWithObjects: @"a", @"ab", @"abc", @"b", nil] - valueForKey: @"length"]) isEqual: [setClass setWithObjects: - [OFNumber numberWithInt: 1], [OFNumber numberWithInt: 2], - [OFNumber numberWithInt: 3], nil]] && - [[set1 valueForKey: @"@count"] isEqual: - [OFNumber numberWithInt: 3]]) - - objc_autoreleasePoolPop(pool); -} - -- (void)setTests -{ - module = @"OFSet"; - [self setTestsWithClass: [SimpleSet class] - mutableClass: [SimpleMutableSet class]]; - - module = @"OFConcreteSet"; - [self setTestsWithClass: [OFConcreteSet class] - mutableClass: [OFConcreteMutableSet class]]; -} @end Index: tests/OFSocketTests.m ================================================================== --- tests/OFSocketTests.m +++ tests/OFSocketTests.m @@ -13,11 +13,15 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFSocketTests: OTTestCase +@end #define COMPARE_V6(a, a0, a1, a2, a3, a4, a5, a6, a7) \ (a.sockaddr.in6.sin6_addr.s6_addr[0] == (a0 >> 8) && \ a.sockaddr.in6.sin6_addr.s6_addr[1] == (a0 & 0xFF) && \ a.sockaddr.in6.sin6_addr.s6_addr[2] == (a1 >> 8) && \ @@ -50,168 +54,199 @@ a.sockaddr.in6.sin6_addr.s6_addr[12] = a6 >> 8; \ a.sockaddr.in6.sin6_addr.s6_addr[13] = a6 & 0xFF; \ a.sockaddr.in6.sin6_addr.s6_addr[14] = a7 >> 8; \ a.sockaddr.in6.sin6_addr.s6_addr[15] = a7 & 0xFF; -static OFString *const module = @"OFSocket"; - -@implementation TestsAppDelegate (OFSocketTests) -- (void)socketTests -{ - void *pool = objc_autoreleasePoolPush(); - OFSocketAddress addr; - - TEST(@"Parsing an IPv4", - R(addr = OFSocketAddressParseIP(@"127.0.0.1", 1234)) && - OFFromBigEndian32(addr.sockaddr.in.sin_addr.s_addr) == 0x7F000001 && - OFFromBigEndian16(addr.sockaddr.in.sin_port) == 1234) - - EXPECT_EXCEPTION(@"Refusing invalid IPv4 #1", OFInvalidFormatException, - OFSocketAddressParseIP(@"127.0.0.0.1", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv4 #2", OFInvalidFormatException, - OFSocketAddressParseIP(@"127.0.0.256", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv4 #3", OFInvalidFormatException, - OFSocketAddressParseIP(@"127.0.0. 1", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv4 #4", OFInvalidFormatException, - OFSocketAddressParseIP(@" 127.0.0.1", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv4 #5", OFInvalidFormatException, - OFSocketAddressParseIP(@"127.0.a.1", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv4 #6", OFInvalidFormatException, - OFSocketAddressParseIP(@"127.0..1", 1234)) - - TEST(@"Port of an IPv4 address", OFSocketAddressIPPort(&addr) == 1234) - - TEST(@"Converting an IPv4 to a string", - [OFSocketAddressString(&addr) isEqual: @"127.0.0.1"]) - - TEST(@"Parsing an IPv6 #1", - R(addr = OFSocketAddressParseIP( - @"1122:3344:5566:7788:99aa:bbCc:ddee:ff00", 1234)) && - COMPARE_V6(addr, - 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00) && - OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234) - - TEST(@"Parsing an IPv6 #2", - R(addr = OFSocketAddressParseIP(@"::", 1234)) && - COMPARE_V6(addr, 0, 0, 0, 0, 0, 0, 0, 0) && - OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234) - - TEST(@"Parsing an IPv6 #3", - R(addr = OFSocketAddressParseIP(@"aaAa::bBbb", 1234)) && - COMPARE_V6(addr, 0xAAAA, 0, 0, 0, 0, 0, 0, 0xBBBB) && - OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234) - - TEST(@"Parsing an IPv6 #4", - R(addr = OFSocketAddressParseIP(@"aaAa::", 1234)) && - COMPARE_V6(addr, 0xAAAA, 0, 0, 0, 0, 0, 0, 0) && - OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234) - - TEST(@"Parsing an IPv6 #5", - R(addr = OFSocketAddressParseIP(@"::aaAa", 1234)) && - COMPARE_V6(addr, 0, 0, 0, 0, 0, 0, 0, 0xAAAA) && - OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234) - - TEST(@"Parsing an IPv6 #6", - R(addr = OFSocketAddressParseIP(@"fd00::1%123", 1234)) && - COMPARE_V6(addr, 0xFD00, 0, 0, 0, 0, 0, 0, 1) && - OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234 && - addr.sockaddr.in6.sin6_scope_id == 123) - - TEST(@"Parsing an IPv6 #7", - R(addr = OFSocketAddressParseIP(@"::ffff:127.0.0.1", 1234)) && - COMPARE_V6(addr, 0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1) && - OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234) - - TEST(@"Parsing an IPv6 #8", - R(addr = OFSocketAddressParseIP(@"64:ff9b::127.0.0.1", 1234)) && - COMPARE_V6(addr, 0x64, 0xFF9B, 0, 0, 0, 0, 0x7F00, 1) && - OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234) - - EXPECT_EXCEPTION(@"Refusing invalid IPv6 #1", OFInvalidFormatException, - OFSocketAddressParseIP(@"1:::2", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv6 #2", OFInvalidFormatException, - OFSocketAddressParseIP(@"1: ::2", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv6 #3", OFInvalidFormatException, - OFSocketAddressParseIP(@"1:: :2", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv6 #4", OFInvalidFormatException, - OFSocketAddressParseIP(@"1::2::3", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv6 #5", OFInvalidFormatException, - OFSocketAddressParseIP(@"10000::1", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv6 #6", OFInvalidFormatException, - OFSocketAddressParseIP(@"::10000", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv6 #7", OFInvalidFormatException, - OFSocketAddressParseIP(@"::1::", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv6 #8", OFInvalidFormatException, - OFSocketAddressParseIP(@"1:2:3:4:5:6:7:", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv6 #9", OFInvalidFormatException, - OFSocketAddressParseIP(@"1:2:3:4:5:6:7::", 1234)) - - EXPECT_EXCEPTION(@"Refusing invalid IPv6 #10", OFInvalidFormatException, - OFSocketAddressParseIP(@"1:2", 1234)) - - TEST(@"Port of an IPv6 address", OFSocketAddressIPPort(&addr) == 1234) - - addr.sockaddr.in6.sin6_scope_id = 0; - - SET_V6(addr, 0, 0, 0, 0, 0, 0, 0, 0) - TEST(@"Converting an IPv6 to a string #1", - [OFSocketAddressString(&addr) isEqual: @"::"]) - - SET_V6(addr, 0, 0, 0, 0, 0, 0, 0, 1) - TEST(@"Converting an IPv6 to a string #2", - [OFSocketAddressString(&addr) isEqual: @"::1"]) - - SET_V6(addr, 1, 0, 0, 0, 0, 0, 0, 0) - TEST(@"Converting an IPv6 to a string #3", - [OFSocketAddressString(&addr) isEqual: @"1::"]) - - SET_V6(addr, - 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00) - TEST(@"Converting an IPv6 to a string #4", - [OFSocketAddressString(&addr) - isEqual: @"1122:3344:5566:7788:99aa:bbcc:ddee:ff00"]) - - SET_V6(addr, 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0) - TEST(@"Converting an IPv6 to a string #5", - [OFSocketAddressString(&addr) - isEqual: @"1122:3344:5566:7788:99aa:bbcc:ddee:0"]) - - SET_V6(addr, 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0, 0) - TEST(@"Converting an IPv6 to a string #6", - [OFSocketAddressString(&addr) - isEqual: @"1122:3344:5566:7788:99aa:bbcc::"]) - - SET_V6(addr, 0, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00) - TEST(@"Converting an IPv6 to a string #7", - [OFSocketAddressString(&addr) - isEqual: @"0:3344:5566:7788:99aa:bbcc:ddee:ff00"]) - - SET_V6(addr, 0, 0, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00) - TEST(@"Converting an IPv6 to a string #8", - [OFSocketAddressString(&addr) - isEqual: @"::5566:7788:99aa:bbcc:ddee:ff00"]) - - SET_V6(addr, 0, 0, 0x5566, 0, 0, 0, 0xDDEE, 0xFF00) - TEST(@"Converting an IPv6 to a string #9", - [OFSocketAddressString(&addr) isEqual: @"0:0:5566::ddee:ff00"]) - - SET_V6(addr, 0, 0, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0, 0) - TEST(@"Converting an IPv6 to a string #10", - [OFSocketAddressString(&addr) - isEqual: @"::5566:7788:99aa:bbcc:0:0"]) - - objc_autoreleasePoolPop(pool); +@implementation OFSocketTests +- (void)testParseIPv4 +{ + OFSocketAddress address = OFSocketAddressParseIP(@"127.0.0.1", 1234); + + OTAssertEqual(OFFromBigEndian32(address.sockaddr.in.sin_addr.s_addr), + 0x7F000001); + OTAssertEqual(OFFromBigEndian16(address.sockaddr.in.sin_port), 1234); +} + +- (void)testParseRejectsInvalidIPv4 +{ + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"127.0.0.0.1", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"127.0.0.256", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"127.0.0. 1", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@" 127.0.0.1", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"127.0.a.1", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"127.0..1", 1234), + OFInvalidFormatException); +} + +- (void)testPortForIPv4 +{ + OFSocketAddress address = OFSocketAddressParseIP(@"127.0.0.1", 1234); + + OTAssertEqual(OFSocketAddressIPPort(&address), 1234); +} + +- (void)testStringForIPv4 +{ + OFSocketAddress address = OFSocketAddressParseIP(@"127.0.0.1", 1234); + + OTAssertEqualObjects(OFSocketAddressString(&address), @"127.0.0.1"); +} + +- (void)testParseIPv6 +{ + OFSocketAddress address; + + address = OFSocketAddressParseIP( + @"1122:3344:5566:7788:99aa:bbCc:ddee:ff00", 1234); + OTAssert(COMPARE_V6(address, + 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00)); + OTAssertEqual(OFFromBigEndian16(address.sockaddr.in6.sin6_port), 1234); + + address = OFSocketAddressParseIP(@"::", 1234); + OTAssert(COMPARE_V6(address, 0, 0, 0, 0, 0, 0, 0, 0)); + OTAssertEqual(OFFromBigEndian16(address.sockaddr.in6.sin6_port), 1234); + + address = OFSocketAddressParseIP(@"aaAa::bBbb", 1234); + OTAssert(COMPARE_V6(address, 0xAAAA, 0, 0, 0, 0, 0, 0, 0xBBBB)); + OTAssertEqual(OFFromBigEndian16(address.sockaddr.in6.sin6_port), 1234); + + address = OFSocketAddressParseIP(@"aaAa::", 1234); + OTAssert(COMPARE_V6(address, 0xAAAA, 0, 0, 0, 0, 0, 0, 0)); + OTAssertEqual(OFFromBigEndian16(address.sockaddr.in6.sin6_port), 1234); + + address = OFSocketAddressParseIP(@"::aaAa", 1234); + OTAssert(COMPARE_V6(address, 0, 0, 0, 0, 0, 0, 0, 0xAAAA)); + OTAssertEqual(OFFromBigEndian16(address.sockaddr.in6.sin6_port), 1234); + + address = OFSocketAddressParseIP(@"fd00::1%123", 1234); + OTAssert(COMPARE_V6(address, 0xFD00, 0, 0, 0, 0, 0, 0, 1)); + OTAssertEqual(OFFromBigEndian16(address.sockaddr.in6.sin6_port), 1234); + OTAssertEqual(address.sockaddr.in6.sin6_scope_id, 123); + + address = OFSocketAddressParseIP(@"::ffff:127.0.0.1", 1234); + OTAssert(COMPARE_V6(address, 0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1)); + OTAssertEqual(OFFromBigEndian16(address.sockaddr.in6.sin6_port), 1234); + + address = OFSocketAddressParseIP(@"64:ff9b::127.0.0.1", 1234); + OTAssert(COMPARE_V6(address, 0x64, 0xFF9B, 0, 0, 0, 0, 0x7F00, 1)); + OTAssertEqual(OFFromBigEndian16(address.sockaddr.in6.sin6_port), 1234); +} + +- (void)testParseRejectsInvalidIPv6 +{ + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1:::2", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1: ::2", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1:: :2", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1::2::3", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"10000::1", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"::10000", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"::1::", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1:2:3:4:5:6:7:", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1:2:3:4:5:6:7::", 1234), + OFInvalidFormatException); + + OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1:2", 1234), + OFInvalidFormatException); +} + +- (void)testPortForIPv6 +{ + OFSocketAddress address = OFSocketAddressParseIP(@"::", 1234); + + OTAssertEqual(OFSocketAddressIPPort(&address), 1234); +} + +- (void)testStringForIPv6 +{ + OFSocketAddress address = OFSocketAddressParseIP(@"::", 1234); + + OTAssertEqualObjects(OFSocketAddressString(&address), @"::"); + + SET_V6(address, 0, 0, 0, 0, 0, 0, 0, 1) + OTAssertEqualObjects(OFSocketAddressString(&address), @"::1"); + + SET_V6(address, 1, 0, 0, 0, 0, 0, 0, 0) + OTAssertEqualObjects(OFSocketAddressString(&address), @"1::"); + + SET_V6(address, + 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00) + OTAssertEqualObjects(OFSocketAddressString(&address), + @"1122:3344:5566:7788:99aa:bbcc:ddee:ff00"); + + SET_V6(address, + 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0) + OTAssertEqualObjects(OFSocketAddressString(&address), + @"1122:3344:5566:7788:99aa:bbcc:ddee:0"); + + SET_V6(address, 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0, 0) + OTAssertEqualObjects(OFSocketAddressString(&address), + @"1122:3344:5566:7788:99aa:bbcc::"); + + SET_V6(address, + 0, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00) + OTAssertEqualObjects(OFSocketAddressString(&address), + @"0:3344:5566:7788:99aa:bbcc:ddee:ff00"); + + SET_V6(address, 0, 0, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00) + OTAssertEqualObjects(OFSocketAddressString(&address), + @"::5566:7788:99aa:bbcc:ddee:ff00"); + + SET_V6(address, 0, 0, 0x5566, 0, 0, 0, 0xDDEE, 0xFF00) + OTAssertEqualObjects(OFSocketAddressString(&address), + @"0:0:5566::ddee:ff00"); + + SET_V6(address, 0, 0, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0, 0) + OTAssertEqualObjects(OFSocketAddressString(&address), + @"::5566:7788:99aa:bbcc:0:0"); + + address.sockaddr.in6.sin6_scope_id = 123; + OTAssertEqualObjects(OFSocketAddressString(&address), + @"::5566:7788:99aa:bbcc:0:0%123"); +} + +- (void)testAddressEqual +{ + OFSocketAddress addr1 = OFSocketAddressParseIP(@"127.0.0.1", 1234); + OFSocketAddress addr2 = OFSocketAddressParseIP(@"127.0.0.1", 1234); + OFSocketAddress addr3 = OFSocketAddressParseIP(@"127.0.0.1", 1235); + + OTAssertTrue(OFSocketAddressEqual(&addr1, &addr2)); + OTAssertFalse(OFSocketAddressEqual(&addr1, &addr3)); +} + +- (void)testAddressHash +{ + OFSocketAddress addr1 = OFSocketAddressParseIP(@"127.0.0.1", 1234); + OFSocketAddress addr2 = OFSocketAddressParseIP(@"127.0.0.1", 1234); + OFSocketAddress addr3 = OFSocketAddressParseIP(@"127.0.0.1", 1235); + + OTAssertEqual(OFSocketAddressHash(&addr1), OFSocketAddressHash(&addr2)); + OTAssertNotEqual(OFSocketAddressHash(&addr1), + OFSocketAddressHash(&addr3)); } @end Index: tests/OFStreamTests.m ================================================================== --- tests/OFStreamTests.m +++ tests/OFStreamTests.m @@ -13,74 +13,75 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFStreamTests: OTTestCase +@end -static OFString *const module = @"OFStream"; +@interface OFTestStream: OFStream +{ + int _state; +} +@end -@interface StreamTest: OFStream +@implementation OFStreamTests +- (void)testStream { - int state; + size_t pageSize = [OFSystemInfo pageSize]; + OFTestStream *stream = [[[OFTestStream alloc] init] autorelease]; + char *cString = OFAllocMemory(pageSize - 2, 1); + + @try { + OFString *string; + + memset(cString, 'X', pageSize - 3); + cString[pageSize - 3] = '\0'; + + OTAssertEqualObjects([stream readLine], @"foo"); + + string = [stream readLine]; + OTAssertNotNil(string); + OTAssertEqual(string.length, pageSize - 3); + OTAssertEqual(strcmp(string.UTF8String, cString), 0); + } @finally { + OFFreeMemory(cString); + } } @end -@implementation StreamTest +@implementation OFTestStream - (bool)lowlevelIsAtEndOfStream { - return (state > 1); + return (_state > 1); } - (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)size { size_t pageSize = [OFSystemInfo pageSize]; - switch (state) { + switch (_state) { case 0: if (size < 1) return 0; memcpy(buffer, "f", 1); - state++; + _state++; return 1; case 1: if (size < pageSize) return 0; memcpy(buffer, "oo\n", 3); memset((char *)buffer + 3, 'X', pageSize - 3); - state++; + _state++; return pageSize; } return 0; } -@end - -@implementation TestsAppDelegate (OFStreamTests) -- (void)streamTests -{ - void *pool = objc_autoreleasePoolPush(); - size_t pageSize = [OFSystemInfo pageSize]; - StreamTest *test = [[[StreamTest alloc] init] autorelease]; - OFString *string; - char *cString; - - cString = OFAllocMemory(pageSize - 2, 1); - memset(cString, 'X', pageSize - 3); - cString[pageSize - 3] = '\0'; - - TEST(@"-[readLine] #1", [[test readLine] isEqual: @"foo"]) - - string = [test readLine]; - TEST(@"-[readLine] #2", string != nil && - string.length == pageSize - 3 && - !strcmp(string.UTF8String, cString)) - - OFFreeMemory(cString); - - objc_autoreleasePoolPop(pool); -} @end ADDED tests/OFStringTests.h Index: tests/OFStringTests.h ================================================================== --- /dev/null +++ tests/OFStringTests.h @@ -0,0 +1,25 @@ +/* + * 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 "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFStringTests: OTTestCase +{ + OFString *_string; +} + +@property (readonly, nonatomic) Class stringClass; +@end Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -17,25 +17,16 @@ #include #include #include -#import "TestsAppDelegate.h" - -#import "OFString.h" -#import "OFMutableUTF8String.h" -#import "OFUTF8String.h" +#import "OFStringTests.h" #ifndef INFINITY # define INFINITY __builtin_inf() #endif -static OFString *module; -static OFString *const whitespace[] = { - @" \r \t\n\t \tasd \t \t\t\r\n", - @" \t\t \t\t \t \t" -}; static const OFUnichar unicharString[] = { 0xFEFF, 'f', 0xF6, 0xF6, 'b', 0xE4, 'r', 0x1F03A, 0 }; static const OFUnichar swappedUnicharString[] = { 0xFFFE0000, 0x66000000, 0xF6000000, 0xF6000000, 0x62000000, 0xE4000000, @@ -47,23 +38,1501 @@ static const OFChar16 swappedChar16String[] = { 0xFFFE, 0x6600, 0xF600, 0xF600, 0x6200, 0xE400, 0x7200, 0x3CD8, 0x3ADC, 0 }; -@interface SimpleString: OFString +@interface CustomString: OFString { OFMutableString *_string; } @end -@interface SimpleMutableString: OFMutableString +@interface CustomMutableString: OFMutableString { OFMutableString *_string; } @end -@implementation SimpleString +@interface EntityHandler: OFObject +@end + +@implementation OFStringTests +- (Class)stringClass +{ + return [CustomString class]; +} + +- (void)setUp +{ + [super setUp]; + + _string = [[self.stringClass alloc] initWithString: @"täṠ€🤔"]; +} + +- (void)dealloc +{ + [_string release]; + + [super dealloc]; +} + +- (void)testIsEqual +{ + OTAssertEqualObjects(_string, @"täṠ€🤔"); + OTAssertEqualObjects(@"täṠ€🤔", _string); + OTAssertNotEqualObjects([self.stringClass stringWithString: @"test"], + @"täṠ€🤔"); + OTAssertNotEqualObjects(@"täṠ€🤔", + [self.stringClass stringWithString: @"test"]); +} + +- (void)testHash +{ + OTAssertEqual(_string.hash, @"täṠ€🤔".hash); + OTAssertNotEqual([[self.stringClass stringWithString: @"test"] hash], + @"täṠ€".hash); +} + +- (void)testCompare +{ + OTAssertEqual([_string compare: @"täṠ€🤔"], OFOrderedSame); + OTAssertEqual([[self.stringClass stringWithString: @""] + compare: @"a"], OFOrderedAscending); + OTAssertEqual([[self.stringClass stringWithString: @"a"] + compare: @"b"], OFOrderedAscending); + OTAssertEqual([[self.stringClass stringWithString: @"cd"] + compare: @"bc"], OFOrderedDescending); + OTAssertEqual([[self.stringClass stringWithString: @"ä"] + compare: @"ö"], OFOrderedAscending); + OTAssertEqual([[self.stringClass stringWithString: @"€"] + compare: @"ß"], OFOrderedDescending); + OTAssertEqual([[self.stringClass stringWithString: @"aa"] + compare: @"z"], OFOrderedAscending); + OTAssertEqual([@"aa" compare: + [self.stringClass stringWithString: @"z"]], OFOrderedAscending); +} + +- (void)testCaseInsensitiveCompare +{ +#ifdef OF_HAVE_UNICODE_TABLES + OTAssertEqual([[self.stringClass stringWithString: @"a"] + caseInsensitiveCompare: @"A"], OFOrderedSame); + OTAssertEqual([[self.stringClass stringWithString: @"Ä"] + caseInsensitiveCompare: @"ä"], OFOrderedSame); + OTAssertEqual([[self.stringClass stringWithString: @"я"] + caseInsensitiveCompare: @"Я"], OFOrderedSame); + OTAssertEqual([[self.stringClass stringWithString: @"€"] + caseInsensitiveCompare: @"ß"], OFOrderedDescending); + OTAssertEqual([[self.stringClass stringWithString: @"ß"] + caseInsensitiveCompare: @"→"], OFOrderedAscending); + OTAssertEqual([[self.stringClass stringWithString: @"AA"] + caseInsensitiveCompare: @"z"], OFOrderedAscending); + OTAssertEqual([[self.stringClass stringWithString: @"ABC"] + caseInsensitiveCompare: @"AbD"], OFOrderedAscending); +#else + OTAssertEqual([[self.stringClass stringWithString: @"a"] + caseInsensitiveCompare: @"A"], OFOrderedSame); + OTAssertEqual([[self.stringClass stringWithString: @"AA"] + caseInsensitiveCompare: @"z"], OFOrderedAscending); + OTAssertEqual([[self.stringClass stringWithString: @"ABC"] + caseInsensitiveCompare: @"AbD"], OFOrderedAscending); +#endif +} + +- (void)testDescription +{ + OTAssertEqualObjects(_string.description, @"täṠ€🤔"); +} + +- (void)testLength +{ + OTAssertEqual(_string.length, 5); +} + +- (void)testUTF8StringLength +{ + OTAssertEqual(_string.UTF8StringLength, 13); +} + +- (void)testCharacterAtIndex +{ + OTAssertEqual([_string characterAtIndex: 0], 't'); + OTAssertEqual([_string characterAtIndex: 1], 0xE4); + OTAssertEqual([_string characterAtIndex: 2], 0x1E60); + OTAssertEqual([_string characterAtIndex: 3], 0x20AC); + OTAssertEqual([_string characterAtIndex: 4], 0x1F914); +} + +- (void)testCharacterAtIndexFailsWithOutOfRangeIndex +{ + OTAssertThrowsSpecific([_string characterAtIndex: 5], + OFOutOfRangeException); +} + +- (void)testUppercaseString +{ +#ifdef OF_HAVE_UNICODE_TABLES + OTAssertEqualObjects(_string.uppercaseString, @"TÄṠ€🤔"); +#else + OTAssertEqualObjects(_string.uppercaseString, @"TäṠ€🤔"); +#endif +} + +- (void)testLowercaseString +{ +#ifdef OF_HAVE_UNICODE_TABLES + OTAssertEqualObjects(_string.lowercaseString, @"täṡ€🤔"); +#else + OTAssertEqualObjects(_string.lowercaseString, @"täṠ€🤔"); +#endif +} + +- (void)testCapitalizedString +{ +#ifdef OF_HAVE_UNICODE_TABLES + OTAssertEqualObjects([[self.stringClass stringWithString: + @"täṠ€🤔täṠ€🤔 täṠ€🤔"] capitalizedString], @"Täṡ€🤔täṡ€🤔 Täṡ€🤔"); +#else + OTAssertEqualObjects([[self.stringClass stringWithString: + @"täṠ€🤔täṠ€🤔 täṠ€🤔"] capitalizedString], @"TäṠ€🤔täṠ€🤔 TäṠ€🤔"); +#endif +} + +- (void)testStringWithUTF8StringLength +{ + OTAssertEqualObjects([self.stringClass + stringWithUTF8String: "\xEF\xBB\xBF" "foobar" + length: 6], @"foo"); +} + +- (void)testStringWithUTF16String +{ + OTAssertEqualObjects([self.stringClass + stringWithUTF16String: char16String], @"fööbär🀺"); + OTAssertEqualObjects([self.stringClass + stringWithUTF16String: swappedChar16String], @"fööbär🀺"); +} + +- (void)testStringWithUTF32String +{ + OTAssertEqualObjects([self.stringClass + stringWithUTF32String: unicharString], @"fööbär🀺"); + OTAssertEqualObjects([self.stringClass + stringWithUTF32String: swappedUnicharString], @"fööbär🀺"); +} + +- (void)testStringWithUTF8StringFailsWithInvalidUTF8 +{ + OTAssertThrowsSpecific( + [self.stringClass stringWithUTF8String: "\xE0\x80"], + OFInvalidEncodingException); + + OTAssertThrowsSpecific( + [self.stringClass stringWithUTF8String: "\xF0\x80\x80\xC0"], + OFInvalidEncodingException); +} + +- (void)testStringWithCStringEncodingISO8859_1 +{ + OTAssertEqualObjects([self.stringClass + stringWithCString: "\xE4\xF6\xFC" + encoding: OFStringEncodingISO8859_1], @"äöü"); +} + +#ifdef HAVE_ISO_8859_15 +- (void)testStringWithCStringEncodingISO8859_15 +{ + OTAssertEqualObjects([self.stringClass + stringWithCString: "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE" + encoding: OFStringEncodingISO8859_15], @"€ŠšŽžŒœŸ"); +} +#endif + +#ifdef HAVE_WINDOWS_1252 +- (void)testStringWithCStringEncodingWindows1252 +{ + OTAssertEqualObjects([self.stringClass + stringWithCString: "\x80\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B" + "\x8C\x8E\x91\x92\x93\x94\x95\x96\x97\x98\x99" + "\x9A\x9B\x9C\x9E\x9F" + encoding: OFStringEncodingWindows1252], + @"€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ"); +} +#endif + +#ifdef HAVE_CODEPAGE_437 +- (void)testStringWithCStringEncodingCodepage437 +{ + OTAssertEqualObjects([self.stringClass + stringWithCString: "\xB0\xB1\xB2\xDB" + encoding: OFStringEncodingCodepage437], @"░▒▓█"); +} +#endif + +#ifdef OF_HAVE_FILES +- (void)testStringWithContentsOfFileEncoding +{ + OTAssertEqualObjects([self.stringClass + stringWithContentsOfFile: @"testfile.txt" + encoding: OFStringEncodingISO8859_1], @"testäöü"); +} + +- (void)testStringWithContentsOfIRIEncoding +{ + OTAssertEqualObjects([self.stringClass + stringWithContentsOfIRI: [OFIRI fileIRIWithPath: @"testfile.txt"] + encoding: OFStringEncodingISO8859_1], @"testäöü"); +} +#endif + +- (void)testCStringWithEncodingASCII +{ + OTAssertEqual(strcmp([[self.stringClass stringWithString: + @"This is a test"] cStringWithEncoding: OFStringEncodingASCII], + "This is a test"), 0); + + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"This is a tést"] cStringWithEncoding: OFStringEncodingASCII], + OFInvalidEncodingException); +} + +- (void)testCStringWithEncodingISO8859_1 +{ + OTAssertEqual(strcmp([[self.stringClass stringWithString: + @"This is ä test"] cStringWithEncoding: OFStringEncodingISO8859_1], + "This is \xE4 test"), 0); + + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"This is ä t€st"] cStringWithEncoding: OFStringEncodingISO8859_1], + OFInvalidEncodingException); +} + +#ifdef HAVE_ISO_8859_15 +- (void)testCStringWithEncodingISO8859_15 +{ + OTAssertEqual(strcmp([[self.stringClass stringWithString: + @"This is ä t€st"] cStringWithEncoding: OFStringEncodingISO8859_15], + "This is \xE4 t\xA4st"), 0); + + OTAssertThrowsSpecific( + [[self.stringClass stringWithString: @"This is ä t€st…"] + cStringWithEncoding: OFStringEncodingISO8859_15], + OFInvalidEncodingException); +} +#endif + +#ifdef HAVE_WINDOWS_1252 +- (void)testCStringWithEncodingWindows1252 +{ + OTAssertEqual( + strcmp([[self.stringClass stringWithString: @"This is ä t€st…"] + cStringWithEncoding: OFStringEncodingWindows1252], + "This is \xE4 t\x80st\x85"), 0); + + OTAssertThrowsSpecific( + [[self.stringClass stringWithString: @"This is ä t€st…‼"] + cStringWithEncoding: OFStringEncodingWindows1252], + OFInvalidEncodingException); +} +#endif + +#ifdef HAVE_CODEPAGE_437 +- (void)testCStringWithEncodingCodepage437 +{ + OTAssertEqual( + strcmp([[self.stringClass stringWithString: @"Tést strîng ░▒▓"] + cStringWithEncoding: OFStringEncodingCodepage437], + "T\x82st str\x8Cng \xB0\xB1\xB2"), 0); + + OTAssertThrowsSpecific( + [[self.stringClass stringWithString: @"T€st strîng ░▒▓"] + cStringWithEncoding: OFStringEncodingCodepage437], + OFInvalidEncodingException); +} +#endif + +- (void)testLossyCStringWithEncodingASCII +{ + OTAssertEqual(strcmp([[self.stringClass stringWithString: + @"This is a tést"] lossyCStringWithEncoding: OFStringEncodingASCII], + "This is a t?st"), 0); +} + +- (void)testLossyCStringWithEncodingISO8859_1 +{ + OTAssertEqual( + strcmp([[self.stringClass stringWithString: @"This is ä t€st"] + lossyCStringWithEncoding: OFStringEncodingISO8859_1], + "This is \xE4 t?st"), 0); +} + +#ifdef HAVE_ISO_8859_15 +- (void)testLossyCStringWithEncodingISO8859_15 +{ + OTAssertEqual( + strcmp([[self.stringClass stringWithString: @"This is ä t€st…"] + lossyCStringWithEncoding: OFStringEncodingISO8859_15], + "This is \xE4 t\xA4st?"), 0); +} +#endif + +#ifdef HAVE_WINDOWS_1252 +- (void)testLossyCStringWithEncodingWindows1252 +{ + OTAssertEqual( + strcmp([[self.stringClass stringWithString: @"This is ä t€st…‼"] + lossyCStringWithEncoding: OFStringEncodingWindows1252], + "This is \xE4 t\x80st\x85?"), 0); +} +#endif + +#ifdef HAVE_CODEPAGE_437 +- (void)testLossyCStringWithEncodingCodepage437 +{ + OTAssertEqual( + strcmp([[self.stringClass stringWithString: @"T€st strîng ░▒▓"] + lossyCStringWithEncoding: OFStringEncodingCodepage437], + "T?st str\x8Cng \xB0\xB1\xB2"), 0); +} +#endif + +- (void)testStringWithFormat +{ + OTAssertEqualObjects( + ([self.stringClass stringWithFormat: @"%@:%d", @"test", 123]), + @"test:123"); +} + +- (void)testRangeOfString +{ + OFString *string = [self.stringClass stringWithString: @"𝄞öö"]; + + OTAssertEqual([string rangeOfString: @"öö"].location, 1); + OTAssertEqual([string rangeOfString: @"ö"].location, 1); + OTAssertEqual([string rangeOfString: @"𝄞"].location, 0); + OTAssertEqual([string rangeOfString: @"x"].location, OFNotFound); + + OTAssertEqual([string + rangeOfString: @"öö" + options: OFStringSearchBackwards].location, 1); + + OTAssertEqual([string + rangeOfString: @"ö" + options: OFStringSearchBackwards].location, 2); + + OTAssertEqual([string + rangeOfString: @"𝄞" + options: OFStringSearchBackwards].location, 0); + + OTAssertEqual([string + rangeOfString: @"x" + options: OFStringSearchBackwards].location, OFNotFound); +} + +- (void)testRangeOfStringFailsWithOutOfRangeRange +{ + OTAssertThrowsSpecific( + [_string rangeOfString: @"t" options: 0 range: OFMakeRange(6, 1)], + OFOutOfRangeException); +} + +- (void)testIndexOfCharacterFromSet +{ + OFCharacterSet *characterSet = + [OFCharacterSet characterSetWithCharactersInString: @"cđ"]; + + OTAssertEqual([[self.stringClass stringWithString: @"abcđabcđe"] + indexOfCharacterFromSet: characterSet], 2); + + OTAssertEqual([[self.stringClass stringWithString: @"abcđabcđë"] + indexOfCharacterFromSet: characterSet + options: OFStringSearchBackwards], 7); + + OTAssertEqual([[self.stringClass stringWithString: @"abcđabcđë"] + indexOfCharacterFromSet: characterSet + options: 0 + range: OFMakeRange(4, 4)], 6); + + OTAssertEqual([[self.stringClass stringWithString: @"abcđabcđëf"] + indexOfCharacterFromSet: characterSet + options: 0 + range: OFMakeRange(8, 2)], OFNotFound); +} + +- (void)testIndexOfCharacterFromSetFailsWithOutOfRangeRange +{ + OFCharacterSet *characterSet = + [OFCharacterSet characterSetWithCharactersInString: @"cđ"]; + + OTAssertThrowsSpecific([[self.stringClass stringWithString: @"𝄞öö"] + indexOfCharacterFromSet: characterSet + options: 0 + range: OFMakeRange(3, 1)], + OFOutOfRangeException); +} + +- (void)testSubstringWithRange +{ + OTAssertEqualObjects([_string substringWithRange: OFMakeRange(1, 2)], + @"äṠ"); + + OTAssertEqualObjects([_string substringWithRange: OFMakeRange(3, 0)], + @""); +} + +- (void)testSubstringWithRangeFailsWithOutOfRangeRange +{ + OTAssertThrowsSpecific([_string substringWithRange: OFMakeRange(4, 2)], + OFOutOfRangeException); + + OTAssertThrowsSpecific([_string substringWithRange: OFMakeRange(6, 0)], + OFOutOfRangeException); +} + +- (void)testStringByAppendingString +{ + OTAssertEqualObjects([_string stringByAppendingString: @"äöü"], + @"täṠ€🤔äöü"); +} + +- (void)testHasPrefix +{ + OTAssertTrue([_string hasPrefix: @"täṠ"]); + OTAssertFalse([_string hasPrefix: @"🤔"]); +} + +- (void)testHasSuffix +{ + OTAssertTrue([_string hasSuffix: @"🤔"]); + OTAssertFalse([_string hasSuffix: @"täṠ"]); +} + +- (void)testComponentsSeparatedByString +{ + OTAssertEqualObjects([[self.stringClass stringWithString: + @"fooXXbarXXXXbazXXXX"] componentsSeparatedByString: @"XX"], + ([OFArray arrayWithObjects: @"foo", @"bar", @"", @"baz", @"", @"", + nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] componentsSeparatedByString: @""], + [OFArray arrayWithObject: @"foo"]); +} + +- (void)testComponentsSeparatedByStringOptions +{ + OTAssertEqualObjects([[self.stringClass stringWithString: + @"fooXXbarXXXXbazXXXX"] + componentsSeparatedByString: @"XX" + options: OFStringSkipEmptyComponents], + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])); +} + +- (void)testComponentsSeparatedByCharactersInSet +{ + OFCharacterSet *characterSet = + [OFCharacterSet characterSetWithCharactersInString: @"XYZ"]; + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"fooXYbarXYZXbazXYXZx"] + componentsSeparatedByCharactersInSet: characterSet], + ([OFArray arrayWithObjects: @"foo", @"", @"bar", @"", @"", @"", + @"baz", @"", @"", @"", @"x", nil])); +} + +- (void)testComponentsSeparatedByCharactersInSetOptions +{ + OFCharacterSet *characterSet = + [OFCharacterSet characterSetWithCharactersInString: @"XYZ"]; + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"fooXYbarXYZXbazXYXZ"] + componentsSeparatedByCharactersInSet: characterSet + options: OFStringSkipEmptyComponents], + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])); +} + +- (void)testLongLongValue +{ + OTAssertEqual([[self.stringClass stringWithString: + @"1234"] longLongValue], 1234); + + OTAssertEqual([[self.stringClass stringWithString: + @"\r\n+123 "] longLongValue], 123); + + OTAssertEqual([[self.stringClass stringWithString: + @"-500\t"] longLongValue], -500); + + OTAssertEqual([[self.stringClass stringWithString: + @"-0x10\t"] longLongValueWithBase: 0], -0x10); + + OTAssertEqual([[self.stringClass stringWithString: + @"\t\t\r\n"] longLongValue], 0); + + OTAssertEqual([[self.stringClass stringWithString: + @"123f"] longLongValueWithBase: 16], 0x123f); + + OTAssertEqual([[self.stringClass stringWithString: + @"-1234"] longLongValueWithBase: 0], -1234); + + OTAssertEqual([[self.stringClass stringWithString: + @"\t\n0xABcd\r"] longLongValueWithBase: 0], 0xABCD); + + OTAssertEqual([[self.stringClass stringWithString: + @"1234567"] longLongValueWithBase: 8], 01234567); + + OTAssertEqual([[self.stringClass stringWithString: + @"\r\n0123"] longLongValueWithBase: 0], 0123); + + OTAssertEqual([[self.stringClass stringWithString: + @"765\t"] longLongValueWithBase: 8], 0765); + + OTAssertEqual([[self.stringClass stringWithString: + @"\t\t\r\n"] longLongValueWithBase: 8], 0); +} + +- (void)testLongLongValueThrowsOnInvalidFormat +{ + OTAssertThrowsSpecific( + [[self.stringClass stringWithString: @"abc"] longLongValue], + OFInvalidFormatException); + + OTAssertThrowsSpecific( + [[self.stringClass stringWithString: @"0a"] longLongValue], + OFInvalidFormatException); + + OTAssertThrowsSpecific( + [[self.stringClass stringWithString: @"0 1"] longLongValue], + OFInvalidFormatException); + + OTAssertThrowsSpecific( + [[self.stringClass stringWithString: @"0xABCDEFG"] + longLongValueWithBase: 0], + OFInvalidFormatException); + + OTAssertThrowsSpecific( + [[self.stringClass stringWithString: @"0x"] + longLongValueWithBase: 0], + OFInvalidFormatException); +} + +- (void)testLongLongValueThrowsOnOutOfRange +{ + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"-12345678901234567890123456789012345678901234567890" + @"12345678901234567890123456789012345678901234567890"] + longLongValueWithBase: 16], OFOutOfRangeException) +} + +- (void)testUnsignedLongLongValue +{ + OTAssertEqual([[self.stringClass stringWithString: + @"1234"] unsignedLongLongValue], 1234); + + OTAssertEqual([[self.stringClass stringWithString: + @"\r\n+123 "] unsignedLongLongValue], 123); + + OTAssertEqual([[self.stringClass stringWithString: + @"\t\t\r\n"] unsignedLongLongValue], 0); + + OTAssertEqual([[self.stringClass stringWithString: + @"123f"] unsignedLongLongValueWithBase: 16], 0x123f); + + OTAssertEqual([[self.stringClass stringWithString: + @"1234"] unsignedLongLongValueWithBase: 0], 1234); + + OTAssertEqual([[self.stringClass stringWithString: + @"\t\n0xABcd\r"] unsignedLongLongValueWithBase: 0], 0xABCD); + + OTAssertEqual([[self.stringClass stringWithString: + @"1234567"] unsignedLongLongValueWithBase: 8], 01234567); + + OTAssertEqual([[self.stringClass stringWithString: + @"\r\n0123"] unsignedLongLongValueWithBase: 0], 0123); + + OTAssertEqual([[self.stringClass stringWithString: @"765\t"] + unsignedLongLongValueWithBase: 8], 0765); + + OTAssertEqual([[self.stringClass stringWithString: @"\t\t\r\n"] + unsignedLongLongValueWithBase: 8], 0); +} + +- (void)testUnsignedLongLongValueThrowsOnOutOfRange +{ + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" + @"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"] + unsignedLongLongValueWithBase: 16], OFOutOfRangeException); +} + +- (void)testFloatValue +{ + /* + * These test numbers can be generated without rounding if we have IEEE + * floating point numbers, thus we can use OTAssertEqual on them. + */ + + OTAssertEqual([[self.stringClass stringWithString: + @"\t-0.25 "] floatValue], -0.25); + + OTAssertEqual([[self.stringClass stringWithString: + @"\r\n\tINF\t\n"] floatValue], INFINITY); + OTAssertEqual([[self.stringClass stringWithString: + @"\r -INFINITY\n"] floatValue], -INFINITY); + + OTAssertTrue(isnan([[self.stringClass stringWithString: + @" NAN\t\t"] floatValue])); + OTAssertTrue(isnan([[self.stringClass stringWithString: + @" -NaN\t\t"] floatValue])); +} + +- (void)testFloatValueThrowsOnInvalidFormat +{ + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"0.0a"] floatValue], OFInvalidFormatException); + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"0 0"] floatValue], OFInvalidFormatException); + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"0,0"] floatValue], OFInvalidFormatException); +} + +- (void)testDoubleValue +{ +#if (defined(OF_SOLARIS) && defined(OF_X86)) || defined(OF_AMIGAOS_M68K) + /* + * Solaris' strtod() has weird rounding on x86, but not on AMD64. + * AmigaOS 3 with libnix has weird rounding as well. + */ + OTAssertEqual([[self.stringClass stringWithString: + @"\t-0.125 "] doubleValue], -0.125); +#elif defined(OF_ANDROID) || defined(OF_SOLARIS) || defined(OF_HPUX) || \ + defined(OF_DJGPP) || defined(OF_AMIGAOS_M68K) + /* + * Android, Solaris, HP-UX, DJGPP and AmigaOS 3 do not accept 0x for + * strtod(). + */ + OTAssertEqual([[self.stringClass stringWithString: + @"\t-0.123456789 "] doubleValue], -0.123456789); +#else + OTAssertEqual([[self.stringClass stringWithString: + @"\t-0x1.FFFFFFFFFFFFFP-1020 "] doubleValue], + -0x1.FFFFFFFFFFFFFP-1020); +#endif + + OTAssertEqual([[self.stringClass stringWithString: + @"\r\n\tINF\t\n"] doubleValue], INFINITY); + OTAssertEqual([[self.stringClass stringWithString: + @"\r -INFINITY\n"] doubleValue], -INFINITY); + + OTAssert(isnan([[self.stringClass stringWithString: + @" NAN\t\t"] doubleValue])); + OTAssert(isnan([[self.stringClass stringWithString: + @" -NaN\t\t"] doubleValue])); +} + +- (void)testDoubleValueThrowsOnInvalidFormat +{ + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"0.0a"] doubleValue], OFInvalidFormatException); + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"0 0"] doubleValue], OFInvalidFormatException); + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"0,0"] doubleValue], OFInvalidFormatException); +} + +- (void)testCharacters +{ + OTAssertEqual(memcmp([[self.stringClass stringWithString: @"fööbär🀺"] + characters], unicharString + 1, sizeof(unicharString) - 8), 0); +} + +- (void)testUTF16String +{ + OFString *string = [self.stringClass stringWithString: @"fööbär🀺"]; + + OTAssertEqual(memcmp(string.UTF16String, char16String + 1, + OFUTF16StringLength(char16String) * 2), 0); + +#ifdef OF_BIG_ENDIAN + OTAssertEqual(memcmp([string UTF16StringWithByteOrder: + OFByteOrderLittleEndian], swappedChar16String + 1, + OFUTF16StringLength(swappedChar16String) * 2), 0); +#else + OTAssertEqual(memcmp([string UTF16StringWithByteOrder: + OFByteOrderBigEndian], swappedChar16String + 1, + OFUTF16StringLength(swappedChar16String) * 2), 0); +#endif +} + +- (void)testUTF16StringLength +{ + OTAssertEqual(_string.UTF16StringLength, 6); +} + +- (void)testUTF32String +{ + OFString *string = [self.stringClass stringWithString: @"fööbär🀺"]; + + OTAssertEqual(memcmp(string.UTF32String, unicharString + 1, + OFUTF32StringLength(unicharString) * 4), 0); + +#ifdef OF_BIG_ENDIAN + OTAssertEqual(memcmp([string UTF32StringWithByteOrder: + OFByteOrderLittleEndian], swappedUnicharString + 1, + OFUTF32StringLength(swappedUnicharString) * 4), 0); +#else + OTAssertEqual(memcmp([string UTF32StringWithByteOrder: + OFByteOrderBigEndian], swappedUnicharString + 1, + OFUTF32StringLength(swappedUnicharString) * 4), 0); +#endif +} + +- (void)testStringByMD5Hashing +{ + OTAssertEqualObjects(_string.stringByMD5Hashing, + @"7e6bef5fe100d93e808d15b1c6e6145a"); +} + +- (void)testStringByRIPEMD160Hashing +{ + OTAssertEqualObjects(_string.stringByRIPEMD160Hashing, + @"2fd0ec899c55cf2821a2f844b9d80887fc351103"); +} + +- (void)testStringBySHA1Hashing +{ + OTAssertEqualObjects(_string.stringBySHA1Hashing, + @"3f76f9358b372b7147344b7a3ba6d309e4466b3a"); +} + +- (void)testStringBySHA224Hashing +{ + OTAssertEqualObjects(_string.stringBySHA224Hashing, + @"6e57ec72e4da55c46d88a15ce7ce4d8db83d0493a263134a3734259d"); +} + +- (void)testStringBySHA256Hashing +{ + OTAssertEqualObjects(_string.stringBySHA256Hashing, + @"6eac4d3d0b4152c82ff88599482696ca" + @"d6dca0b533e8a2e6963d995b19b0a683"); +} + +- (void)testStringBySHA384Hashing +{ + OTAssertEqualObjects(_string.stringBySHA384Hashing, + @"d9bd6a671407d01cee4022888677040d" + @"108dd0270c38e0ce755d6dcadb4bf9c1" + @"89204dd2a51f954be55ea5d5fe00667b"); +} + +- (void)testStringBySHA512Hashing +{ + OTAssertEqualObjects(_string.stringBySHA512Hashing, + @"64bec66b3633c585da6d32760fa3617a" + @"47ca4c247472bdbbfb452b2dbf5a3612" + @"5629053394a16ecd08f8a21d461537c5" + @"f1224cbb379589e73dcd6763ec4f886c"); +} + +- (void)testStringByAddingPercentEncodingWithAllowedCharacters +{ + OFCharacterSet *characterSet = + [OFCharacterSet characterSetWithCharactersInString: @"abfo'_~$🍏"]; + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo\"ba'_~$]🍏🍌"] + stringByAddingPercentEncodingWithAllowedCharacters: characterSet], + @"foo%22ba'_~$%5D🍏%F0%9F%8D%8C"); +} + +- (void)testStringByRemovingPercentEncoding +{ + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo%20bar%22+%24%F0%9F%8D%8C"] stringByRemovingPercentEncoding], + @"foo bar\"+$🍌"); +} + +- (void)testStringByRemovingPercentEncodingThrowsOnInvalidFormat +{ + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"foo%xbar"] stringByRemovingPercentEncoding], + OFInvalidFormatException); +} + +- (void)testStringByRemovingPercentEncodingThrowsOnInvalidEncoding +{ + OTAssertThrowsSpecific([[self.stringClass stringWithString: + @"foo%FFbar"] stringByRemovingPercentEncoding], + OFInvalidEncodingException); +} + +- (void)testStringByXMLEscaping +{ + OTAssertEqualObjects([[self.stringClass stringWithString: + @" &world'\"!&"] stringByXMLEscaping], + @"<hello> &world'"!&"); +} + +- (void)testStringByXMLUnescaping +{ + OTAssertEqualObjects([[self.stringClass stringWithString: + @"<hello> &world'"!&"] + stringByXMLUnescaping], + @" &world'\"!&"); + + OTAssertEqualObjects([[self.stringClass stringWithString: @"y"] + stringByXMLUnescaping], @"y"); + OTAssertEqualObjects([[self.stringClass stringWithString: @"ä"] + stringByXMLUnescaping], @"ä"); + OTAssertEqualObjects([[self.stringClass stringWithString: @"€"] + stringByXMLUnescaping], @"€"); + OTAssertEqualObjects([[self.stringClass stringWithString: @"𝄞"] + stringByXMLUnescaping], @"𝄞"); +} + +- (void)testStringByXMLUnescapingThrowsOnUnknownEntities +{ + OTAssertThrowsSpecific([[self.stringClass stringWithString: @"&foo;"] + stringByXMLUnescaping], OFUnknownXMLEntityException); +} + +- (void)testStringByXMLUnescapingThrowsOnInvalidFormat +{ + OTAssertThrowsSpecific([[self.stringClass stringWithString: @"x&"] + stringByXMLUnescaping], OFInvalidFormatException); + OTAssertThrowsSpecific([[self.stringClass stringWithString: @"&#;"] + stringByXMLUnescaping], OFInvalidFormatException); + OTAssertThrowsSpecific([[self.stringClass stringWithString: @"&#x;"] + stringByXMLUnescaping], OFInvalidFormatException); + OTAssertThrowsSpecific([[self.stringClass stringWithString: @"&#g;"] + stringByXMLUnescaping], OFInvalidFormatException); + OTAssertThrowsSpecific([[self.stringClass stringWithString: @"&#xg;"] + stringByXMLUnescaping], OFInvalidFormatException); +} + +- (void)testStringByXMLUnescapingWithDelegate +{ + EntityHandler *entityHandler = + [[[EntityHandler alloc] init] autorelease]; + + OTAssertEqualObjects([[self.stringClass stringWithString: @"x&foo;y"] + stringByXMLUnescapingWithDelegate: entityHandler], + @"xbary"); +} + +#ifdef OF_HAVE_BLOCKS +- (void)testStringByXMLUnescapingWithBlock +{ + OTAssertEqualObjects([[self.stringClass stringWithString: @"x&foo;y"] + stringByXMLUnescapingWithBlock: ^ OFString * (OFString *string, + OFString *entity) { + if ([entity isEqual: @"foo"]) + return @"bar"; + + return nil; + }], @"xbary"); +} + +- (void)testEnumerateLinesUsingBlock +{ + __block size_t count = 0; + + [[self.stringClass stringWithString: @"foo\nbar\nbaz"] + enumerateLinesUsingBlock: ^ (OFString *line, bool *stop) { + switch (count++) { + case 0: + OTAssertEqualObjects(line, @"foo"); + break; + case 1: + OTAssertEqualObjects(line, @"bar"); + break; + case 2: + OTAssertEqualObjects(line, @"baz"); + break; + default: + OTAssert(false); + } + }]; + + OTAssertEqual(count, 3); +} +#endif + +#ifdef OF_HAVE_FILES +- (void)testIsAbsolutePath +{ +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + OTAssertTrue( + [[self.stringClass stringWithString: @"C:\\foo"] isAbsolutePath]); + OTAssertTrue( + [[self.stringClass stringWithString: @"a:/foo"] isAbsolutePath]); + OTAssertFalse( + [[self.stringClass stringWithString: @"foo"] isAbsolutePath]); + OTAssertFalse( + [[self.stringClass stringWithString: @"b:foo"] isAbsolutePath]); +# ifdef OF_WINDOWS + OTAssertTrue( + [[self.stringClass stringWithString: @"\\\\foo"] isAbsolutePath]); +# endif +# elif defined(OF_AMIGAOS) + OTAssertTrue( + [[self.stringClass stringWithString: @"dh0:foo"] isAbsolutePath]); + OTAssertTrue( + [[self.stringClass stringWithString: @"dh0:a/b"] isAbsolutePath]); + OTAssertFalse( + [[self.stringClass stringWithString: @"foo/bar"] isAbsolutePath]); + OTAssertFalse( + [[self.stringClass stringWithString: @"foo"] isAbsolutePath]); +# elif defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) || \ + defined(OF_WII) || defined(OF_NINTENDO_SWITCH) + OTAssertTrue( + [[self.stringClass stringWithString: @"sdmc:/foo"] isAbsolutePath]); + OTAssertFalse( + [[self.stringClass stringWithString: @"sdmc:foo"] isAbsolutePath]); + OTAssertFalse( + [[self.stringClass stringWithString: @"foo/bar"] isAbsolutePath]); + OTAssertFalse( + [[self.stringClass stringWithString: @"foo"] isAbsolutePath]); +# else + OTAssertTrue( + [[self.stringClass stringWithString: @"/foo"] isAbsolutePath]); + OTAssertTrue( + [[self.stringClass stringWithString: @"/foo/bar"] isAbsolutePath]); + OTAssertFalse( + [[self.stringClass stringWithString: @"foo/bar"] isAbsolutePath]); + OTAssertFalse( + [[self.stringClass stringWithString: @"foo"] isAbsolutePath]); +# endif +} + +- (void)testStringByAppendingPathComponent +{ +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + OTAssertEqualObjects([[self.stringClass stringWithString: @"foo\\bar"] + stringByAppendingPathComponent: @"baz"], + @"foo\\bar\\baz"); + + OTAssertEqualObjects([[self.stringClass stringWithString: @"foo\\bar\\"] + stringByAppendingPathComponent: @"baz"], + @"foo\\bar\\baz"); +# else + OTAssertEqualObjects([[self.stringClass stringWithString: @"foo/bar"] + stringByAppendingPathComponent: @"baz"], + @"foo/bar/baz"); + + OTAssertEqualObjects([[self.stringClass stringWithString: @"foo/bar/"] + stringByAppendingPathComponent: @"baz"], + @"foo/bar/baz"); +# endif +} + +- (void)testStringByAppendingPathExtension +{ +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] stringByAppendingPathExtension: @"bar"], + @"foo.bar"); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:\\tmp\\foo"] stringByAppendingPathExtension: @"bar"], + @"c:\\tmp\\foo.bar"); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:\\tmp\\/\\"] stringByAppendingPathExtension: @"bar"], + @"c:\\tmp.bar"); +# elif defined(OF_AMIGAOS) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] stringByAppendingPathExtension: @"bar"], + @"foo.bar"); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] stringByAppendingPathExtension: @"baz"], + @"foo/bar.baz"); +# else + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] stringByAppendingPathExtension: @"bar"], + @"foo.bar"); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] stringByAppendingPathExtension: @"baz"], + @"foo/bar.baz"); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo///"] stringByAppendingPathExtension: @"bar"], + @"foo.bar"); +# endif +} + +- (void)testPathWithComponents +{ +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])], + @"foo\\bar\\baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"c:\\", @"foo", @"bar", @"baz", nil])], + @"c:\\foo\\bar\\baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"c:", @"foo", @"bar", @"baz", nil])], + @"c:foo\\bar\\baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"c:", @"\\", @"foo", @"bar", @"baz", + nil])], @"c:\\foo\\bar\\baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"c:", @"/", @"foo", @"bar", @"baz", + nil])], @"c:/foo\\bar\\baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"foo/", @"bar\\", @"", @"baz", @"\\", + nil])], @"foo/bar\\baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + [OFArray arrayWithObject: @"foo"]], @"foo"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + [OFArray arrayWithObject: @"c:"]], @"c:"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + [OFArray arrayWithObject: @"c:\\"]], @"c:\\"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + [OFArray arrayWithObject: @"\\"]], @"\\"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + [OFArray arrayWithObject: @"/"]], @"/"); + +# ifdef OF_WINDOWS + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"\\\\", @"foo", @"bar", nil])], + @"\\\\foo\\bar"); +# endif +# elif defined(OF_AMIGAOS) + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"dh0:", @"foo", @"bar", @"baz", nil])], + @"dh0:foo/bar/baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])], + @"foo/bar/baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"foo/", @"bar", @"", @"baz", @"/", + nil])], @"foo//bar/baz//"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + [OFArray arrayWithObject: @"foo"]], @"foo"); +# elif defined(OF_WII) || defined(OF_NINTENDO_DS) || \ + defined(OF_NINTENDO_3DS) || defined(OF_NINTENDO_SWITCH) + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])], + @"foo/bar/baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"sdmc:", @"foo", @"bar", @"baz", + nil])], @"sdmc:/foo/bar/baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"foo/", @"bar/", @"", @"baz", @"/", + nil])], @"foo/bar/baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + [OFArray arrayWithObject: @"foo"]], @"foo"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + [OFArray arrayWithObject: @"sdmc:"]], @"sdmc:/"); +# else + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"/", @"foo", @"bar", @"baz", nil])], + @"/foo/bar/baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])], + @"foo/bar/baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + ([OFArray arrayWithObjects: @"foo/", @"bar", @"", @"baz", @"/", + nil])], @"foo/bar/baz"); + + OTAssertEqualObjects([self.stringClass pathWithComponents: + [OFArray arrayWithObject: @"foo"]], @"foo"); +# endif +} + +- (void)testPathComponents +{ +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:/tmp"] pathComponents], + ([OFArray arrayWithObjects: @"c:/", @"tmp", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:\\tmp\\"] pathComponents], + ([OFArray arrayWithObjects: @"c:\\", @"tmp", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:\\"] pathComponents], [OFArray arrayWithObject: @"c:\\"]); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:/"] pathComponents], [OFArray arrayWithObject: @"c:/"]); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:"] pathComponents], [OFArray arrayWithObject: @"c:"]); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo\\bar"] pathComponents], + ([OFArray arrayWithObjects: @"foo", @"bar", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo\\bar/baz/"] pathComponents], + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo\\/"] pathComponents], [OFArray arrayWithObject: @"foo"]); + +# ifdef OF_WINDOWS + OTAssertEqualObjects([[self.stringClass stringWithString: + @"\\\\foo\\bar"] pathComponents], + ([OFArray arrayWithObjects: @"\\\\", @"foo", @"bar", nil])); +# endif + + OTAssertEqualObjects([[self.stringClass stringWithString: @""] + pathComponents], [OFArray array]); +# elif defined(OF_AMIGAOS) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:tmp"] pathComponents], + ([OFArray arrayWithObjects: @"dh0:", @"tmp", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:tmp/"] pathComponents], + ([OFArray arrayWithObjects: @"dh0:", @"tmp", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:/"] pathComponents], + ([OFArray arrayWithObjects: @"dh0:", @"/", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] pathComponents], + ([OFArray arrayWithObjects: @"foo", @"bar", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar/baz/"] pathComponents], + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo//"] pathComponents], + ([OFArray arrayWithObjects: @"foo", @"/", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: @""] + pathComponents], [OFArray array]); +# elif defined(OF_NINTENDO_3DS) || defined(OF_WII) || \ + defined(OF_NINTENDO_SWITCH) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"sdmc:/tmp"] pathComponents], + ([OFArray arrayWithObjects: @"sdmc:", @"tmp", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"sdmc:/"] pathComponents], [OFArray arrayWithObject: @"sdmc:"]); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] pathComponents], + ([OFArray arrayWithObjects: @"foo", @"bar", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar/baz/"] pathComponents], + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo//"] pathComponents], [OFArray arrayWithObject: @"foo"]); + + OTAssertEqualObjects([[self.stringClass stringWithString: @""] + pathComponents], [OFArray array]); +# else + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/tmp"] pathComponents], + ([OFArray arrayWithObjects: @"/", @"tmp", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/tmp/"] pathComponents], + ([OFArray arrayWithObjects: @"/", @"tmp", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/"] pathComponents], [OFArray arrayWithObject: @"/"]); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] pathComponents], + ([OFArray arrayWithObjects: @"foo", @"bar", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar/baz/"] pathComponents], + ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil])); + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo//"] pathComponents], [OFArray arrayWithObject: @"foo"]); + + OTAssertEqualObjects([[self.stringClass stringWithString: @""] + pathComponents], [OFArray array]); +# endif +} + +- (void)testLastPathComponent +{ +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:/tmp"] lastPathComponent], @"tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:\\tmp\\"] lastPathComponent], @"tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:\\"] lastPathComponent], @"c:\\"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:/"] lastPathComponent], @"c:/"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"\\"] lastPathComponent], @"\\"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] lastPathComponent], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo\\bar"] lastPathComponent], @"bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar/baz/"] lastPathComponent], @"baz"); +# ifdef OF_WINDOWS + OTAssertEqualObjects([[self.stringClass stringWithString: + @"\\\\foo\\bar"] lastPathComponent], @"bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"\\\\"] lastPathComponent], @"\\\\"); +# endif +# elif defined(OF_AMIGAOS) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:tmp"] lastPathComponent], @"tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:tmp/"] lastPathComponent], @"tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:/"] lastPathComponent], @"/"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:"] lastPathComponent], @"dh0:"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] lastPathComponent], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] lastPathComponent], @"bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar/baz/"] lastPathComponent], @"baz"); +# elif defined(OF_WII) || defined(OF_NINTENDO_DS) || \ + defined(OF_NINTENDO_3DS) || defined(OF_NINTENDO_SWITCH) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"sdmc:/tmp"] lastPathComponent], @"tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"sdmc:/tmp/"] lastPathComponent], @"tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"sdmc:/"] lastPathComponent], @"sdmc:/"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"sdmc:"] lastPathComponent], @"sdmc:"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] lastPathComponent], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] lastPathComponent], @"bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar/baz/"] lastPathComponent], @"baz"); +# else + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/tmp"] lastPathComponent], @"tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/tmp/"] lastPathComponent], @"tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/"] lastPathComponent], @"/"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] lastPathComponent], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] lastPathComponent], @"bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar/baz/"] lastPathComponent], @"baz"); +# endif +} + +- (void)testPathExtension +{ + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo.bar"] pathExtension], @"bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/.bar"] pathExtension], @""); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/.bar.baz"] pathExtension], @"baz"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar.baz/"] pathExtension], @"baz"); +} + +- (void)testStringByDeletingLastPathComponent +{ +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"\\tmp"] stringByDeletingLastPathComponent], @"\\"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/tmp/"] stringByDeletingLastPathComponent], @"/"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:\\"] stringByDeletingLastPathComponent], @"c:\\"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:/"] stringByDeletingLastPathComponent], @"c:/"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:\\tmp/foo/"] stringByDeletingLastPathComponent], @"c:\\tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo\\bar"] stringByDeletingLastPathComponent], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"\\"] stringByDeletingLastPathComponent], @"\\"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] stringByDeletingLastPathComponent], @"."); +# ifdef OF_WINDOWS + OTAssertEqualObjects([[self.stringClass stringWithString: + @"\\\\foo\\bar"] stringByDeletingLastPathComponent], @"\\\\foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"\\\\foo"] stringByDeletingLastPathComponent], @"\\\\"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"\\\\"] stringByDeletingLastPathComponent], @"\\\\"); +# endif +# elif defined(OF_AMIGAOS) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:"] stringByDeletingLastPathComponent], @"dh0:"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:tmp"] stringByDeletingLastPathComponent], @"dh0:"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:tmp/"] stringByDeletingLastPathComponent], @"dh0:"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:/"] stringByDeletingLastPathComponent], @"dh0:"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:tmp/foo/"] stringByDeletingLastPathComponent], @"dh0:tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] stringByDeletingLastPathComponent], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] stringByDeletingLastPathComponent], @""); +# elif defined(OF_NINTENDO_3DS) || defined(OF_WII) || \ + defined(OF_NINTENDO_SWITCH) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/tmp/"] stringByDeletingLastPathComponent], @""); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"sdmc:/tmp/foo/"] stringByDeletingLastPathComponent], + @"sdmc:/tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"sdmc:/"] stringByDeletingLastPathComponent], @"sdmc:/"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] stringByDeletingLastPathComponent], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/"] stringByDeletingLastPathComponent], @""); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] stringByDeletingLastPathComponent], @"."); +# else + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/tmp"] stringByDeletingLastPathComponent], @"/"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/tmp/"] stringByDeletingLastPathComponent], @"/"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/tmp/foo/"] stringByDeletingLastPathComponent], @"/tmp"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo/bar"] stringByDeletingLastPathComponent], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/"] stringByDeletingLastPathComponent], @"/"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo"] stringByDeletingLastPathComponent], @"."); +# endif +} + +- (void)testStringByDeletingPathExtension +{ +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo.bar"] stringByDeletingPathExtension], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo..bar"] stringByDeletingPathExtension], @"foo."); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:/foo.\\bar"] stringByDeletingPathExtension], @"c:/foo.\\bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:\\foo./bar.baz"] stringByDeletingPathExtension], + @"c:\\foo.\\bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo.bar/"] stringByDeletingPathExtension], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @".foo"] stringByDeletingPathExtension], @".foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @".foo.bar"] stringByDeletingPathExtension], @".foo"); +# elif defined(OF_AMIGAOS) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo.bar"] stringByDeletingPathExtension], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo..bar"] stringByDeletingPathExtension], @"foo."); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:foo.bar"] stringByDeletingPathExtension], @"dh0:foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:foo./bar"] stringByDeletingPathExtension], @"dh0:foo./bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"dh0:foo./bar.baz"] stringByDeletingPathExtension], + @"dh0:foo./bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo.bar/"] stringByDeletingPathExtension], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @".foo"] stringByDeletingPathExtension], @".foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @".foo\\bar"] stringByDeletingPathExtension], @".foo\\bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @".foo.bar"] stringByDeletingPathExtension], @".foo"); +# elif defined(OF_WII) || defined(OF_NINTENDO_DS) || \ + defined(OF_NINTENDO_3DS) || defined(OF_NINTENDO_SWITCH) + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo.bar"] stringByDeletingPathExtension], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo..bar"] stringByDeletingPathExtension], @"foo."); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"sdmc:/foo./bar"] stringByDeletingPathExtension], + @"sdmc:/foo./bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"sdmc:/foo./bar.baz"] stringByDeletingPathExtension], + @"sdmc:/foo./bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo.bar/"] stringByDeletingPathExtension], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @".foo"] stringByDeletingPathExtension], @".foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @".foo.bar"] stringByDeletingPathExtension], @".foo"); +# else + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo.bar"] stringByDeletingPathExtension], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo..bar"] stringByDeletingPathExtension], @"foo."); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/foo./bar"] stringByDeletingPathExtension], @"/foo./bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"/foo./bar.baz"] stringByDeletingPathExtension], @"/foo./bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @"foo.bar/"] stringByDeletingPathExtension], @"foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @".foo"] stringByDeletingPathExtension], @".foo"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @".foo\\bar"] stringByDeletingPathExtension], @".foo\\bar"); + OTAssertEqualObjects([[self.stringClass stringWithString: + @".foo.bar"] stringByDeletingPathExtension], @".foo"); +# endif +} + +# if defined(OF_WINDOWS) || defined(OF_MSDOS) +- (void)testStringByStandardizingPath +{ + /* TODO: Add more tests */ + + OTAssertEqualObjects([[self.stringClass stringWithString: + @"c:\\..\\asd"] stringByStandardizingPath], + @"c:\\..\\asd"); + +# ifndef OF_MSDOS + OTAssertEqualObjects([[self.stringClass stringWithString: + @"\\\\foo\\..\\bar\\qux"] stringByStandardizingPath], + @"\\\\bar\\qux"); +# endif +} +# endif +#endif +@end + +@implementation CustomString - (instancetype)init { self = [super init]; @try { @@ -178,1315 +1647,15 @@ { return _string.length; } @end -@implementation SimpleMutableString -+ (void)initialize -{ - if (self == [SimpleMutableString class]) - [self inheritMethodsFromClass: [SimpleString class]]; -} - -- (void)replaceCharactersInRange: (OFRange)range - withString: (OFString *)string -{ - [_string replaceCharactersInRange: range withString: string]; -} -@end - -@interface EntityHandler: OFObject -@end - @implementation EntityHandler - (OFString *)string: (OFString *)string containsUnknownEntityNamed: (OFString *)entity { if ([entity isEqual: @"foo"]) return @"bar"; return nil; } -@end - -@implementation TestsAppDelegate (OFStringTests) -- (void)stringTestsWithClass: (Class)stringClass - mutableClass: (Class)mutableStringClass -{ - void *pool = objc_autoreleasePoolPush(); - OFMutableString *mutableString1, *mutableString2, *mutableString3; - OFString *string; - OFArray *array; - size_t i; - const OFUnichar *characters; - const uint16_t *UTF16Characters; - OFCharacterSet *characterSet; - EntityHandler *entityHandler; -#ifdef OF_HAVE_BLOCKS - __block int j; - __block bool ok; -#endif - -#define C(s) ((OFString *)[stringClass stringWithString: s]) - - mutableString1 = [mutableStringClass stringWithString: @"täs€"]; - mutableString2 = [mutableStringClass string]; - mutableString3 = [[mutableString1 copy] autorelease]; - - TEST(@"-[isEqual:]", [mutableString1 isEqual: mutableString3] && - ![mutableString1 isEqual: [[[OFObject alloc] init] autorelease]]) - - TEST(@"-[compare:]", - [mutableString1 compare: mutableString3] == OFOrderedSame && - [mutableString1 compare: @""] != OFOrderedSame && - [C(@"") compare: @"a"] == OFOrderedAscending && - [C(@"a") compare: @"b"] == OFOrderedAscending && - [C(@"cd") compare: @"bc"] == OFOrderedDescending && - [C(@"ä") compare: @"ö"] == OFOrderedAscending && - [C(@"€") compare: @"ß"] == OFOrderedDescending && - [C(@"aa") compare: @"z"] == OFOrderedAscending) - -#ifdef OF_HAVE_UNICODE_TABLES - TEST(@"-[caseInsensitiveCompare:]", - [C(@"a") caseInsensitiveCompare: @"A"] == OFOrderedSame && - [C(@"Ä") caseInsensitiveCompare: @"ä"] == OFOrderedSame && - [C(@"я") caseInsensitiveCompare: @"Я"] == OFOrderedSame && - [C(@"€") caseInsensitiveCompare: @"ß"] == OFOrderedDescending && - [C(@"ß") caseInsensitiveCompare: @"→"] == OFOrderedAscending && - [C(@"AA") caseInsensitiveCompare: @"z"] == OFOrderedAscending && - [[stringClass stringWithUTF8String: "ABC"] caseInsensitiveCompare: - [stringClass stringWithUTF8String: "AbD"]] == - [C(@"abc") compare: @"abd"]) -#else - TEST(@"-[caseInsensitiveCompare:]", - [C(@"a") caseInsensitiveCompare: @"A"] == OFOrderedSame && - [C(@"AA") caseInsensitiveCompare: @"z"] == OFOrderedAscending && - [[stringClass stringWithUTF8String: "ABC"] caseInsensitiveCompare: - [stringClass stringWithUTF8String: "AbD"]] == - [C(@"abc") compare: @"abd"]) -#endif - - TEST(@"-[hash] is the same if -[isEqual:] is true", - mutableString1.hash == mutableString3.hash) - - TEST(@"-[description]", - [mutableString1.description isEqual: mutableString1]) - - TEST(@"-[appendString:] and -[appendUTF8String:]", - R([mutableString2 appendUTF8String: "1𝄞"]) && - R([mutableString2 appendString: @"3"]) && - R([mutableString1 appendString: mutableString2]) && - [mutableString1 isEqual: @"täs€1𝄞3"]) - - TEST(@"-[appendCharacters:length:]", - R([mutableString2 appendCharacters: unicharString + 6 length: 2]) && - [mutableString2 isEqual: @"1𝄞3r🀺"]) - - TEST(@"-[length]", mutableString1.length == 7) - TEST(@"-[UTF8StringLength]", mutableString1.UTF8StringLength == 13) - TEST(@"-[hash]", mutableString1.hash == 0x705583C0) - - TEST(@"-[characterAtIndex:]", - [mutableString1 characterAtIndex: 0] == 't' && - [mutableString1 characterAtIndex: 1] == 0xE4 && - [mutableString1 characterAtIndex: 3] == 0x20AC && - [mutableString1 characterAtIndex: 5] == 0x1D11E) - - EXPECT_EXCEPTION(@"Detect out of range in -[characterAtIndex:]", - OFOutOfRangeException, [mutableString1 characterAtIndex: 7]) - - mutableString2 = [mutableStringClass stringWithString: @"abc"]; - -#ifdef OF_HAVE_UNICODE_TABLES - TEST(@"-[uppercase]", R([mutableString1 uppercase]) && - [mutableString1 isEqual: @"TÄS€1𝄞3"] && - R([mutableString2 uppercase]) && [mutableString2 isEqual: @"ABC"]) - - TEST(@"-[lowercase]", R([mutableString1 lowercase]) && - [mutableString1 isEqual: @"täs€1𝄞3"] && - R([mutableString2 lowercase]) && [mutableString2 isEqual: @"abc"]) - - TEST(@"-[uppercaseString]", - [[mutableString1 uppercaseString] isEqual: @"TÄS€1𝄞3"]) - - TEST(@"-[lowercaseString]", R([mutableString1 uppercase]) && - [[mutableString1 lowercaseString] isEqual: @"täs€1𝄞3"]) - - TEST(@"-[capitalizedString]", [C(@"džbla tdžst TDŽST").capitalizedString - isEqual: @"Džbla Tdžst Tdžst"]) -#else - TEST(@"-[uppercase]", R([mutableString1 uppercase]) && - [mutableString1 isEqual: @"3𝄞1€SäT"] && - R([mutableString2 uppercase]) && [mutableString2 isEqual: @"ABC"]) - - TEST(@"-[lowercase]", R([mutableString1 lowercase]) && - [mutableString1 isEqual: @"3𝄞1€sät"] && - R([mutableString2 lowercase]) && [mutableString2 isEqual: @"abc"]) - - TEST(@"-[uppercaseString]", - [mutableString1.uppercaseString isEqual: @"3𝄞1€SäT"]) - - TEST(@"-[lowercaseString]", - R([mutableString1 uppercase]) && - [mutableString1.lowercaseString isEqual: @"3𝄞1€sät"]) - - TEST(@"-[capitalizedString]", [C(@"džbla tdžst TDŽST").capitalizedString - isEqual: @"džbla Tdžst TDŽst"]) -#endif - - TEST(@"+[stringWithUTF8String:length:]", - (mutableString1 = [mutableStringClass - stringWithUTF8String: "\xEF\xBB\xBF" "foobar" - length: 6]) && - [mutableString1 isEqual: @"foo"]) - - TEST(@"+[stringWithUTF16String:]", - (string = [stringClass stringWithUTF16String: char16String]) && - [string isEqual: @"fööbär🀺"] && - (string = [stringClass stringWithUTF16String: - swappedChar16String]) && [string isEqual: @"fööbär🀺"]) - - TEST(@"+[stringWithUTF32String:]", - (string = [stringClass stringWithUTF32String: unicharString]) && - [string isEqual: @"fööbär🀺"] && - (string = [stringClass stringWithUTF32String: - swappedUnicharString]) && [string isEqual: @"fööbär🀺"]) - -#ifdef OF_HAVE_FILES - TEST(@"+[stringWithContentsOfFile:encoding]", (string = [stringClass - stringWithContentsOfFile: @"testfile.txt" - encoding: OFStringEncodingISO8859_1]) && - [string isEqual: @"testäöü"]) - - TEST(@"+[stringWithContentsOfIRI:encoding]", (string = [stringClass - stringWithContentsOfIRI: [OFIRI fileIRIWithPath: @"testfile.txt"] - encoding: OFStringEncodingISO8859_1]) && - [string isEqual: @"testäöü"]) -#endif - - TEST(@"-[appendUTFString:length:]", - R([mutableString1 appendUTF8String: "\xEF\xBB\xBF" "barqux" - length: 6]) && - [mutableString1 isEqual: @"foobar"]) - - EXPECT_EXCEPTION(@"Detection of invalid UTF-8 encoding #1", - OFInvalidEncodingException, - [stringClass stringWithUTF8String: "\xE0\x80"]) - EXPECT_EXCEPTION(@"Detection of invalid UTF-8 encoding #2", - OFInvalidEncodingException, - [stringClass stringWithUTF8String: "\xF0\x80\x80\xC0"]) - - TEST(@"Conversion of ISO 8859-1 to Unicode", - [[stringClass stringWithCString: "\xE4\xF6\xFC" - encoding: OFStringEncodingISO8859_1] - isEqual: @"äöü"]) - -#ifdef HAVE_ISO_8859_15 - TEST(@"Conversion of ISO 8859-15 to Unicode", - [[stringClass stringWithCString: "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE" - encoding: OFStringEncodingISO8859_15] - isEqual: @"€ŠšŽžŒœŸ"]) -#endif - -#ifdef HAVE_WINDOWS_1252 - TEST(@"Conversion of Windows 1252 to Unicode", - [[stringClass stringWithCString: "\x80\x82\x83\x84\x85\x86\x87\x88" - "\x89\x8A\x8B\x8C\x8E\x91\x92\x93" - "\x94\x95\x96\x97\x98\x99\x9A\x9B" - "\x9C\x9E\x9F" - encoding: OFStringEncodingWindows1252] - isEqual: @"€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ"]) -#endif - -#ifdef HAVE_CODEPAGE_437 - TEST(@"Conversion of Codepage 437 to Unicode", - [[stringClass stringWithCString: "\xB0\xB1\xB2\xDB" - encoding: OFStringEncodingCodepage437] - isEqual: @"░▒▓█"]) -#endif - - TEST(@"Conversion of Unicode to ASCII #1", - !strcmp([C(@"This is a test") cStringWithEncoding: - OFStringEncodingASCII], "This is a test")) - - EXPECT_EXCEPTION(@"Conversion of Unicode to ASCII #2", - OFInvalidEncodingException, - [C(@"This is a tést") - cStringWithEncoding: OFStringEncodingASCII]) - - TEST(@"Conversion of Unicode to ISO-8859-1 #1", - !strcmp([C(@"This is ä test") cStringWithEncoding: - OFStringEncodingISO8859_1], "This is \xE4 test")) - - EXPECT_EXCEPTION(@"Conversion of Unicode to ISO-8859-1 #2", - OFInvalidEncodingException, - [C(@"This is ä t€st") cStringWithEncoding: - OFStringEncodingISO8859_1]) - -#ifdef HAVE_ISO_8859_15 - TEST(@"Conversion of Unicode to ISO-8859-15 #1", - !strcmp([C(@"This is ä t€st") cStringWithEncoding: - OFStringEncodingISO8859_15], "This is \xE4 t\xA4st")) - - EXPECT_EXCEPTION(@"Conversion of Unicode to ISO-8859-15 #2", - OFInvalidEncodingException, - [C(@"This is ä t€st…") cStringWithEncoding: - OFStringEncodingISO8859_15]) -#endif - -#ifdef HAVE_WINDOWS_1252 - TEST(@"Conversion of Unicode to Windows-1252 #1", - !strcmp([C(@"This is ä t€st…") cStringWithEncoding: - OFStringEncodingWindows1252], "This is \xE4 t\x80st\x85")) - - EXPECT_EXCEPTION(@"Conversion of Unicode to Windows-1252 #2", - OFInvalidEncodingException, [C(@"This is ä t€st…‼") - cStringWithEncoding: OFStringEncodingWindows1252]) -#endif - -#ifdef HAVE_CODEPAGE_437 - TEST(@"Conversion of Unicode to Codepage 437 #1", - !strcmp([C(@"Tést strîng ░▒▓") cStringWithEncoding: - OFStringEncodingCodepage437], "T\x82st str\x8Cng \xB0\xB1\xB2")) - - EXPECT_EXCEPTION(@"Conversion of Unicode to Codepage 437 #2", - OFInvalidEncodingException, [C(@"T€st strîng ░▒▓") - cStringWithEncoding: OFStringEncodingCodepage437]) -#endif - - TEST(@"Lossy conversion of Unicode to ASCII", - !strcmp([C(@"This is a tést") lossyCStringWithEncoding: - OFStringEncodingASCII], "This is a t?st")) - - TEST(@"Lossy conversion of Unicode to ISO-8859-1", - !strcmp([C(@"This is ä t€st") lossyCStringWithEncoding: - OFStringEncodingISO8859_1], "This is \xE4 t?st")) - -#ifdef HAVE_ISO_8859_15 - TEST(@"Lossy conversion of Unicode to ISO-8859-15", - !strcmp([C(@"This is ä t€st…") lossyCStringWithEncoding: - OFStringEncodingISO8859_15], "This is \xE4 t\xA4st?")) -#endif - -#ifdef HAVE_WINDOWS_1252 - TEST(@"Lossy conversion of Unicode to Windows-1252", - !strcmp([C(@"This is ä t€st…‼") lossyCStringWithEncoding: - OFStringEncodingWindows1252], "This is \xE4 t\x80st\x85?")) -#endif - -#ifdef HAVE_CODEPAGE_437 - TEST(@"Lossy conversion of Unicode to Codepage 437", - !strcmp([C(@"T€st strîng ░▒▓") lossyCStringWithEncoding: - OFStringEncodingCodepage437], "T?st str\x8Cng \xB0\xB1\xB2")) -#endif - - TEST(@"+[stringWithFormat:]", - [(mutableString1 = [mutableStringClass stringWithFormat: @"%@:%d", - @"test", - 123]) - isEqual: @"test:123"]) - - TEST(@"-[appendFormat:]", - R(([mutableString1 appendFormat: @"%02X", 15])) && - [mutableString1 isEqual: @"test:1230F"]) - - TEST(@"-[rangeOfString:]", - [C(@"𝄞öö") rangeOfString: @"öö"].location == 1 && - [C(@"𝄞öö") rangeOfString: @"ö"].location == 1 && - [C(@"𝄞öö") rangeOfString: @"𝄞"].location == 0 && - [C(@"𝄞öö") rangeOfString: @"x"].location == OFNotFound && - [C(@"𝄞öö") rangeOfString: @"öö" - options: OFStringSearchBackwards].location == 1 && - [C(@"𝄞öö") rangeOfString: @"ö" - options: OFStringSearchBackwards].location == 2 && - [C(@"𝄞öö") rangeOfString: @"𝄞" - options: OFStringSearchBackwards].location == 0 && - [C(@"𝄞öö") rangeOfString: @"x" - options: OFStringSearchBackwards].location == OFNotFound) - - EXPECT_EXCEPTION( - @"Detect out of range in -[rangeOfString:options:range:]", - OFOutOfRangeException, - [C(@"𝄞öö") rangeOfString: @"ö" options: 0 range: OFMakeRange(3, 1)]) - - characterSet = - [OFCharacterSet characterSetWithCharactersInString: @"cđ"]; - TEST(@"-[indexOfCharacterFromSet:]", - [C(@"abcđabcđe") indexOfCharacterFromSet: characterSet] == 2 && - [C(@"abcđabcđë") - indexOfCharacterFromSet: characterSet - options: OFStringSearchBackwards] == 7 && - [C(@"abcđabcđë") indexOfCharacterFromSet: characterSet - options: 0 - range: OFMakeRange(4, 4)] == 6 && - [C(@"abcđabcđëf") - indexOfCharacterFromSet: characterSet - options: 0 - range: OFMakeRange(8, 2)] == OFNotFound) - - EXPECT_EXCEPTION( - @"Detect out of range in -[indexOfCharacterFromSet:options:range:]", - OFOutOfRangeException, - [C(@"𝄞öö") indexOfCharacterFromSet: characterSet - options: 0 - range: OFMakeRange(3, 1)]) - - TEST(@"-[substringWithRange:]", - [[C(@"𝄞öö") substringWithRange: OFMakeRange(1, 1)] isEqual: @"ö"] && - [[C(@"𝄞öö") substringWithRange: OFMakeRange(3, 0)] isEqual: @""]) - - EXPECT_EXCEPTION(@"Detect out of range in -[substringWithRange:] #1", - OFOutOfRangeException, - [C(@"𝄞öö") substringWithRange: OFMakeRange(2, 2)]) - EXPECT_EXCEPTION(@"Detect out of range in -[substringWithRange:] #2", - OFOutOfRangeException, - [C(@"𝄞öö") substringWithRange: OFMakeRange(4, 0)]) - - TEST(@"-[stringByAppendingString:]", - [[C(@"foo") stringByAppendingString: @"bar"] isEqual: @"foobar"]) - -#ifdef OF_HAVE_FILES -# if defined(OF_WINDOWS) - TEST(@"-[isAbsolutePath]", - C(@"C:\\foo").absolutePath && C(@"a:/foo").absolutePath && - !C(@"foo").absolutePath && !C(@"b:foo").absolutePath && - C(@"\\\\foo").absolutePath) -# elif defined(OF_MSDOS) - TEST(@"-[isAbsolutePath]", - C(@"C:\\foo").absolutePath && C(@"a:/foo").absolutePath && - !C(@"foo").absolutePath && !C(@"b:foo").absolutePath) -# elif defined(OF_AMIGAOS) - TEST(@"-[isAbsolutePath]", - C(@"dh0:foo").absolutePath && C(@"dh0:a/b").absolutePath && - !C(@"foo/bar").absolutePath && !C(@"foo").absolutePath) -# elif defined(OF_NINTENDO_3DS) || defined(OF_WII) || \ - defined(OF_NINTENDO_SWITCH) - TEST(@"-[isAbsolutePath]", - C(@"sdmc:/foo").absolutePath && !C(@"sdmc:foo").absolutePath && - !C(@"foo/bar").absolutePath && !C(@"foo").absolutePath) -# else - TEST(@"-[isAbsolutePath]", - C(@"/foo").absolutePath && C(@"/foo/bar").absolutePath && - !C(@"foo/bar").absolutePath && !C(@"foo").absolutePath) -# endif - - mutableString1 = [mutableStringClass stringWithString: @"foo"]; -# if defined(OF_WINDOWS) || defined(OF_MSDOS) - [mutableString1 appendString: @"\\"]; -# else - [mutableString1 appendString: @"/"]; -# endif - [mutableString1 appendString: @"bar"]; - mutableString2 = [mutableStringClass stringWithString: mutableString1]; -# if defined(OF_WINDOWS) || defined(OF_MSDOS) - [mutableString2 appendString: @"\\"]; -# else - [mutableString2 appendString: @"/"]; -# endif - string = [stringClass stringWithString: mutableString2]; - [mutableString2 appendString: @"baz"]; - TEST(@"-[stringByAppendingPathComponent:]", - [[mutableString1 stringByAppendingPathComponent: @"baz"] - isEqual: mutableString2] && - [[string stringByAppendingPathComponent: @"baz"] - isEqual: mutableString2]) - -# if defined(OF_WINDOWS) || defined(OF_MSDOS) - TEST(@"-[stringByAppendingPathExtension:]", - [[C(@"foo") stringByAppendingPathExtension: @"bar"] - isEqual: @"foo.bar"] && - [[C(@"c:\\tmp\\foo") stringByAppendingPathExtension: @"bar"] - isEqual: @"c:\\tmp\\foo.bar"] && - [[C(@"c:\\tmp\\/\\") stringByAppendingPathExtension: @"bar"] - isEqual: @"c:\\tmp.bar"]) -# elif defined(OF_AMIGAOS) - TEST(@"-[stringByAppendingPathExtension:]", - [[C(@"foo") stringByAppendingPathExtension: @"bar"] - isEqual: @"foo.bar"] && - [[C(@"foo/bar") stringByAppendingPathExtension: @"baz"] - isEqual: @"foo/bar.baz"]) -# else - TEST(@"-[stringByAppendingPathExtension:]", - [[C(@"foo") stringByAppendingPathExtension: @"bar"] - isEqual: @"foo.bar"] && - [[C(@"foo/bar") stringByAppendingPathExtension: @"baz"] - isEqual: @"foo/bar.baz"] && - [[C(@"foo///") stringByAppendingPathExtension: @"bar"] - isEqual: @"foo.bar"]) -# endif -#endif - - TEST(@"-[hasPrefix:]", [C(@"foobar") hasPrefix: @"foo"] && - ![C(@"foobar") hasPrefix: @"foobar0"]) - - TEST(@"-[hasSuffix:]", [C(@"foobar") hasSuffix: @"bar"] && - ![C(@"foobar") hasSuffix: @"foobar0"]) - - i = 0; - TEST(@"-[componentsSeparatedByString:]", - (array = [C(@"fooXXbarXXXXbazXXXX") - componentsSeparatedByString: @"XX"]) && - [[array objectAtIndex: i++] isEqual: @"foo"] && - [[array objectAtIndex: i++] isEqual: @"bar"] && - [[array objectAtIndex: i++] isEqual: @""] && - [[array objectAtIndex: i++] isEqual: @"baz"] && - [[array objectAtIndex: i++] isEqual: @""] && - [[array objectAtIndex: i++] isEqual: @""] && - array.count == i && - (array = [C(@"foo") componentsSeparatedByString: @""]) && - [[array objectAtIndex: 0] isEqual: @"foo"] && - array.count == 1) - - i = 0; - TEST(@"-[componentsSeparatedByString:options:]", - (array = [C(@"fooXXbarXXXXbazXXXX") - componentsSeparatedByString: @"XX" - options: OFStringSkipEmptyComponents]) && - [[array objectAtIndex: i++] isEqual: @"foo"] && - [[array objectAtIndex: i++] isEqual: @"bar"] && - [[array objectAtIndex: i++] isEqual: @"baz"] && - array.count == i) - - characterSet = - [OFCharacterSet characterSetWithCharactersInString: @"XYZ"]; - - i = 0; - TEST(@"-[componentsSeparatedByCharactersInSet:]", - (array = [C(@"fooXYbarXYZXbazXYXZx") - componentsSeparatedByCharactersInSet: characterSet]) && - [[array objectAtIndex: i++] isEqual: @"foo"] && - [[array objectAtIndex: i++] isEqual: @""] && - [[array objectAtIndex: i++] isEqual: @"bar"] && - [[array objectAtIndex: i++] isEqual: @""] && - [[array objectAtIndex: i++] isEqual: @""] && - [[array objectAtIndex: i++] isEqual: @""] && - [[array objectAtIndex: i++] isEqual: @"baz"] && - [[array objectAtIndex: i++] isEqual: @""] && - [[array objectAtIndex: i++] isEqual: @""] && - [[array objectAtIndex: i++] isEqual: @""] && - [[array objectAtIndex: i++] isEqual: @"x"] && - array.count == i) - - i = 0; - TEST(@"-[componentsSeparatedByCharactersInSet:options:]", - (array = [C(@"fooXYbarXYZXbazXYXZ") - componentsSeparatedByCharactersInSet: characterSet - options: OFStringSkipEmptyComponents]) && - [[array objectAtIndex: i++] isEqual: @"foo"] && - [[array objectAtIndex: i++] isEqual: @"bar"] && - [[array objectAtIndex: i++] isEqual: @"baz"] && - array.count == i) - -#ifdef OF_HAVE_FILES -# if defined(OF_WINDOWS) - TEST(@"+[pathWithComponents:]", - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", @"bar", @"baz", nil]] isEqual: @"foo\\bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"c:\\", @"foo", @"bar", @"baz", nil]] - isEqual: @"c:\\foo\\bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"c:", @"foo", @"bar", @"baz", nil]] - isEqual: @"c:foo\\bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"c:", @"\\", @"foo", @"bar", @"baz", nil]] - isEqual: @"c:\\foo\\bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"c:", @"/", @"foo", @"bar", @"baz", nil]] - isEqual: @"c:/foo\\bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo/", @"bar\\", @"", @"baz", @"\\", nil]] - isEqual: @"foo/bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", nil]] isEqual: @"foo"] && - [[stringClass pathWithComponents: [OFArray arrayWithObject: @"c:"]] - isEqual: @"c:"] && - [[stringClass pathWithComponents: - [OFArray arrayWithObject: @"c:\\"]] isEqual: @"c:\\"] && - [[stringClass pathWithComponents: - [OFArray arrayWithObject: @"\\"]] isEqual: @"\\"] && - [[stringClass pathWithComponents: - [OFArray arrayWithObject: @"/"]] isEqual: @"/"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"\\\\", @"foo", @"bar", nil]] isEqual: @"\\\\foo\\bar"]) -# elif defined(OF_MSDOS) - TEST(@"+[pathWithComponents:]", - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", @"bar", @"baz", nil]] isEqual: @"foo\\bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"c:\\", @"foo", @"bar", @"baz", nil]] - isEqual: @"c:\\foo\\bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"c:", @"foo", @"bar", @"baz", nil]] - isEqual: @"c:foo\\bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"c:", @"\\", @"foo", @"bar", @"baz", nil]] - isEqual: @"c:\\foo\\bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"c:", @"/", @"foo", @"bar", @"baz", nil]] - isEqual: @"c:/foo\\bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo/", @"bar\\", @"", @"baz", @"\\", nil]] - isEqual: @"foo/bar\\baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", nil]] isEqual: @"foo"] && - [[stringClass pathWithComponents: [OFArray arrayWithObject: @"c:"]] - isEqual: @"c:"] && - [[stringClass pathWithComponents: - [OFArray arrayWithObject: @"c:\\"]] isEqual: @"c:\\"] && - [[stringClass pathWithComponents: - [OFArray arrayWithObject: @"\\"]] isEqual: @"\\"] && - [[stringClass pathWithComponents: - [OFArray arrayWithObject: @"/"]] isEqual: @"/"]) -# elif defined(OF_AMIGAOS) - TEST(@"+[pathWithComponents:]", - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"dh0:", @"foo", @"bar", @"baz", nil]] - isEqual: @"dh0:foo/bar/baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", @"bar", @"baz", nil]] isEqual: @"foo/bar/baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo/", @"bar", @"", @"baz", @"/", nil]] - isEqual: @"foo//bar/baz//"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", nil]] isEqual: @"foo"]) -# elif defined(OF_NINTENDO_3DS) || defined(OF_WII) || \ - defined(OF_NINTENDO_SWITCH) - TEST(@"+[pathWithComponents:]", - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", @"bar", @"baz", nil]] isEqual: @"foo/bar/baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"sdmc:", @"foo", @"bar", @"baz", nil]] - isEqual: @"sdmc:/foo/bar/baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo/", @"bar/", @"", @"baz", @"/", nil]] - isEqual: @"foo/bar/baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", nil]] isEqual: @"foo"] && - [[stringClass pathWithComponents: [OFArray arrayWithObject: - @"sdmc:"]] isEqual: @"sdmc:/"]) -# else - TEST(@"+[pathWithComponents:]", - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"/", @"foo", @"bar", @"baz", nil]] isEqual: @"/foo/bar/baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", @"bar", @"baz", nil]] isEqual: @"foo/bar/baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo/", @"bar", @"", @"baz", @"/", nil]] - isEqual: @"foo/bar/baz"] && - [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", nil]] isEqual: @"foo"]) -# endif - -# if defined(OF_WINDOWS) - TEST(@"-[pathComponents]", - /* c:/tmp */ - (array = C(@"c:/tmp").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"c:/"] && - [[array objectAtIndex: 1] isEqual: @"tmp"] && - /* c:\tmp\ */ - (array = C(@"c:\\tmp\\").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"c:\\"] && - [[array objectAtIndex: 1] isEqual: @"tmp"] && - /* c:\ */ - (array = C(@"c:\\").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"c:\\"] && - /* c:/ */ - (array = C(@"c:/").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"c:/"] && - /* c: */ - (array = C(@"c:").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"c:"] && - /* foo\bar */ - (array = C(@"foo\\bar").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"bar"] && - /* foo\bar/baz/ */ - (array = C(@"foo\\bar/baz/").pathComponents) && array.count == 3 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"bar"] && - [[array objectAtIndex: 2] isEqual: @"baz"] && - /* foo\/ */ - (array = C(@"foo\\/").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - /* \\foo\bar */ - (array = C(@"\\\\foo\\bar").pathComponents) && array.count == 3 && - [[array objectAtIndex: 0] isEqual: @"\\\\"] && - [[array objectAtIndex: 1] isEqual: @"foo"] && - [[array objectAtIndex: 2] isEqual: @"bar"] && - C(@"").pathComponents.count == 0) -# elif defined(OF_MSDOS) - TEST(@"-[pathComponents]", - /* c:/tmp */ - (array = C(@"c:/tmp").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"c:/"] && - [[array objectAtIndex: 1] isEqual: @"tmp"] && - /* c:\tmp\ */ - (array = C(@"c:\\tmp\\").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"c:\\"] && - [[array objectAtIndex: 1] isEqual: @"tmp"] && - /* c:\ */ - (array = C(@"c:\\").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"c:\\"] && - /* c:/ */ - (array = C(@"c:/").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"c:/"] && - /* c: */ - (array = C(@"c:").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"c:"] && - /* foo\bar */ - (array = C(@"foo\\bar").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"bar"] && - /* foo\bar/baz/ */ - (array = C(@"foo\\bar/baz/").pathComponents) && array.count == 3 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"bar"] && - [[array objectAtIndex: 2] isEqual: @"baz"] && - /* foo\/ */ - (array = C(@"foo\\/").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - C(@"").pathComponents.count == 0) -# elif defined(OF_AMIGAOS) - TEST(@"-[pathComponents]", - /* dh0:tmp */ - (array = C(@"dh0:tmp").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"dh0:"] && - [[array objectAtIndex: 1] isEqual: @"tmp"] && - /* dh0:tmp/ */ - (array = C(@"dh0:tmp/").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"dh0:"] && - [[array objectAtIndex: 1] isEqual: @"tmp"] && - /* dh0: */ - (array = C(@"dh0:/").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"dh0:"] && - [[array objectAtIndex: 1] isEqual: @"/"] && - /* foo/bar */ - (array = C(@"foo/bar").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"bar"] && - /* foo/bar/baz/ */ - (array = C(@"foo/bar/baz/").pathComponents) && array.count == 3 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"bar"] && - [[array objectAtIndex: 2] isEqual: @"baz"] && - /* foo// */ - (array = C(@"foo//").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"/"] && - C(@"").pathComponents.count == 0) -# elif defined(OF_NINTENDO_3DS) || defined(OF_WII) || \ - defined(OF_NINTENDO_SWITCH) - TEST(@"-[pathComponents]", - /* sdmc:/tmp */ - (array = C(@"sdmc:/tmp").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"sdmc:"] && - [[array objectAtIndex: 1] isEqual: @"tmp"] && - /* sdmc:/ */ - (array = C(@"sdmc:/").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"sdmc:"] && - /* foo/bar */ - (array = C(@"foo/bar").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"bar"] && - /* foo/bar/baz/ */ - (array = C(@"foo/bar/baz/").pathComponents) && array.count == 3 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"bar"] && - [[array objectAtIndex: 2] isEqual: @"baz"] && - /* foo// */ - (array = C(@"foo//").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - C(@"").pathComponents.count == 0) -# else - TEST(@"-[pathComponents]", - /* /tmp */ - (array = C(@"/tmp").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"/"] && - [[array objectAtIndex: 1] isEqual: @"tmp"] && - /* /tmp/ */ - (array = C(@"/tmp/").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"/"] && - [[array objectAtIndex: 1] isEqual: @"tmp"] && - /* / */ - (array = C(@"/").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"/"] && - /* foo/bar */ - (array = C(@"foo/bar").pathComponents) && array.count == 2 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"bar"] && - /* foo/bar/baz/ */ - (array = C(@"foo/bar/baz/").pathComponents) && array.count == 3 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - [[array objectAtIndex: 1] isEqual: @"bar"] && - [[array objectAtIndex: 2] isEqual: @"baz"] && - /* foo// */ - (array = C(@"foo//").pathComponents) && array.count == 1 && - [[array objectAtIndex: 0] isEqual: @"foo"] && - C(@"").pathComponents.count == 0) -# endif - -# if defined(OF_WINDOWS) - TEST(@"-[lastPathComponent]", - [C(@"c:/tmp").lastPathComponent isEqual: @"tmp"] && - [C(@"c:\\tmp\\").lastPathComponent isEqual: @"tmp"] && - [C(@"c:\\").lastPathComponent isEqual: @"c:\\"] && - [C(@"c:/").lastPathComponent isEqual: @"c:/"] && - [C(@"\\").lastPathComponent isEqual: @"\\"] && - [C(@"foo").lastPathComponent isEqual: @"foo"] && - [C(@"foo\\bar").lastPathComponent isEqual: @"bar"] && - [C(@"foo/bar/baz/").lastPathComponent isEqual: @"baz"] && - [C(@"\\\\foo\\bar").lastPathComponent isEqual: @"bar"] && - [C(@"\\\\").lastPathComponent isEqual: @"\\\\"]) -# elif defined(OF_MSDOS) - TEST(@"-[lastPathComponent]", - [C(@"c:/tmp").lastPathComponent isEqual: @"tmp"] && - [C(@"c:\\tmp\\").lastPathComponent isEqual: @"tmp"] && - [C(@"c:\\").lastPathComponent isEqual: @"c:\\"] && - [C(@"c:/").lastPathComponent isEqual: @"c:/"] && - [C(@"\\").lastPathComponent isEqual: @"\\"] && - [C(@"foo").lastPathComponent isEqual: @"foo"] && - [C(@"foo\\bar").lastPathComponent isEqual: @"bar"] && - [C(@"foo/bar/baz/").lastPathComponent isEqual: @"baz"]) -# elif defined(OF_AMIGAOS) - TEST(@"-[lastPathComponent]", - [C(@"dh0:tmp").lastPathComponent isEqual: @"tmp"] && - [C(@"dh0:tmp/").lastPathComponent isEqual: @"tmp"] && - [C(@"dh0:/").lastPathComponent isEqual: @"/"] && - [C(@"dh0:").lastPathComponent isEqual: @"dh0:"] && - [C(@"foo").lastPathComponent isEqual: @"foo"] && - [C(@"foo/bar").lastPathComponent isEqual: @"bar"] && - [C(@"foo/bar/baz/").lastPathComponent isEqual: @"baz"]) -# elif defined(OF_NINTENDO_3DS) || defined(OF_WII) || \ - defined(OF_NINTENDO_SWITCH) - TEST(@"-[lastPathComponent]", - [C(@"sdmc:/tmp").lastPathComponent isEqual: @"tmp"] && - [C(@"sdmc:/tmp/").lastPathComponent isEqual: @"tmp"] && - [C(@"sdmc:/").lastPathComponent isEqual: @"sdmc:/"] && - [C(@"sdmc:").lastPathComponent isEqual: @"sdmc:"] && - [C(@"foo").lastPathComponent isEqual: @"foo"] && - [C(@"foo/bar").lastPathComponent isEqual: @"bar"] && - [C(@"foo/bar/baz/").lastPathComponent isEqual: @"baz"]) -# else - TEST(@"-[lastPathComponent]", - [C(@"/tmp").lastPathComponent isEqual: @"tmp"] && - [C(@"/tmp/").lastPathComponent isEqual: @"tmp"] && - [C(@"/").lastPathComponent isEqual: @"/"] && - [C(@"foo").lastPathComponent isEqual: @"foo"] && - [C(@"foo/bar").lastPathComponent isEqual: @"bar"] && - [C(@"foo/bar/baz/").lastPathComponent isEqual: @"baz"]) -# endif - - TEST(@"-[pathExtension]", - [C(@"foo.bar").pathExtension isEqual: @"bar"] && - [C(@"foo/.bar").pathExtension isEqual: @""] && - [C(@"foo/.bar.baz").pathExtension isEqual: @"baz"] && - [C(@"foo/bar.baz/").pathExtension isEqual: @"baz"]) - -# if defined(OF_WINDOWS) - TEST(@"-[stringByDeletingLastPathComponent]", - [C(@"\\tmp").stringByDeletingLastPathComponent isEqual: @"\\"] && - [C(@"/tmp/").stringByDeletingLastPathComponent isEqual: @"/"] && - [C(@"c:\\").stringByDeletingLastPathComponent isEqual: @"c:\\"] && - [C(@"c:/").stringByDeletingLastPathComponent isEqual: @"c:/"] && - [C(@"c:\\tmp/foo/").stringByDeletingLastPathComponent - isEqual: @"c:\\tmp"] && - [C(@"foo\\bar").stringByDeletingLastPathComponent - isEqual: @"foo"] && - [C(@"\\").stringByDeletingLastPathComponent isEqual: @"\\"] && - [C(@"foo").stringByDeletingLastPathComponent isEqual: @"."] && - [C(@"\\\\foo\\bar").stringByDeletingLastPathComponent - isEqual: @"\\\\foo"] && - [C(@"\\\\foo").stringByDeletingLastPathComponent - isEqual: @"\\\\"] && - [C(@"\\\\").stringByDeletingLastPathComponent isEqual: @"\\\\"]) -# elif defined(OF_MSDOS) - TEST(@"-[stringByDeletingLastPathComponent]", - [C(@"\\tmp").stringByDeletingLastPathComponent isEqual: @"\\"] && - [C(@"/tmp/").stringByDeletingLastPathComponent isEqual: @"/"] && - [C(@"c:\\").stringByDeletingLastPathComponent isEqual: @"c:\\"] && - [C(@"c:/").stringByDeletingLastPathComponent isEqual: @"c:/"] && - [C(@"c:\\tmp/foo/").stringByDeletingLastPathComponent - isEqual: @"c:\\tmp"] && - [C(@"foo\\bar").stringByDeletingLastPathComponent - isEqual: @"foo"] && - [C(@"\\").stringByDeletingLastPathComponent isEqual: @"\\"] && - [C(@"foo").stringByDeletingLastPathComponent isEqual: @"."]) -# elif defined(OF_AMIGAOS) - TEST(@"-[stringByDeletingLastPathComponent]", - [C(@"dh0:").stringByDeletingLastPathComponent isEqual: @"dh0:"] && - [C(@"dh0:tmp").stringByDeletingLastPathComponent - isEqual: @"dh0:"] && - [C(@"dh0:tmp/").stringByDeletingLastPathComponent - isEqual: @"dh0:"] && - [C(@"dh0:/").stringByDeletingLastPathComponent isEqual: @"dh0:"] && - [C(@"dh0:tmp/foo/").stringByDeletingLastPathComponent - isEqual: @"dh0:tmp"] && - [C(@"foo/bar").stringByDeletingLastPathComponent isEqual: @"foo"] && - [C(@"foo").stringByDeletingLastPathComponent isEqual: @""]) -# elif defined(OF_NINTENDO_3DS) || defined(OF_WII) || \ - defined(OF_NINTENDO_SWITCH) - TEST(@"-[stringByDeletingLastPathComponent]", - [C(@"/tmp/").stringByDeletingLastPathComponent isEqual: @""] && - [C(@"sdmc:/tmp/foo/").stringByDeletingLastPathComponent - isEqual: @"sdmc:/tmp"] && - [C(@"sdmc:/").stringByDeletingLastPathComponent - isEqual: @"sdmc:/"] && - [C(@"foo/bar").stringByDeletingLastPathComponent isEqual: @"foo"] && - [C(@"/").stringByDeletingLastPathComponent isEqual: @""] && - [C(@"foo").stringByDeletingLastPathComponent isEqual: @"."]) -# else - TEST(@"-[stringByDeletingLastPathComponent]", - [C(@"/tmp").stringByDeletingLastPathComponent isEqual: @"/"] && - [C(@"/tmp/").stringByDeletingLastPathComponent isEqual: @"/"] && - [C(@"/tmp/foo/").stringByDeletingLastPathComponent - isEqual: @"/tmp"] && - [C(@"foo/bar").stringByDeletingLastPathComponent isEqual: @"foo"] && - [C(@"/").stringByDeletingLastPathComponent isEqual: @"/"] && - [C(@"foo").stringByDeletingLastPathComponent isEqual: @"."]) -# endif - -# if defined(OF_WINDOWS) || defined(OF_MSDOS) - TEST(@"-[stringByDeletingPathExtension]", - [C(@"foo.bar").stringByDeletingPathExtension isEqual: @"foo"] && - [C(@"foo..bar").stringByDeletingPathExtension isEqual: @"foo."] && - [C(@"c:/foo.\\bar").stringByDeletingPathExtension - isEqual: @"c:/foo.\\bar"] && - [C(@"c:\\foo./bar.baz").stringByDeletingPathExtension - isEqual: @"c:\\foo.\\bar"] && - [C(@"foo.bar/").stringByDeletingPathExtension isEqual: @"foo"] && - [C(@".foo").stringByDeletingPathExtension isEqual: @".foo"] && - [C(@".foo.bar").stringByDeletingPathExtension isEqual: @".foo"]) -# elif defined(OF_AMIGAOS) - TEST(@"-[stringByDeletingPathExtension]", - [C(@"foo.bar").stringByDeletingPathExtension isEqual: @"foo"] && - [C(@"foo..bar").stringByDeletingPathExtension isEqual: @"foo."] && - [C(@"dh0:foo.bar").stringByDeletingPathExtension - isEqual: @"dh0:foo"] && - [C(@"dh0:foo./bar").stringByDeletingPathExtension - isEqual: @"dh0:foo./bar"] && - [C(@"dh0:foo./bar.baz").stringByDeletingPathExtension - isEqual: @"dh0:foo./bar"] && - [C(@"foo.bar/").stringByDeletingPathExtension isEqual: @"foo"] && - [C(@".foo").stringByDeletingPathExtension isEqual: @".foo"] && - [C(@".foo\\bar").stringByDeletingPathExtension - isEqual: @".foo\\bar"] && - [C(@".foo.bar").stringByDeletingPathExtension isEqual: @".foo"]) -# elif defined(OF_NINTENDO_3DS) || defined(OF_WII) || \ - defined(OF_NINTENDO_SWITCH) - TEST(@"-[stringByDeletingPathExtension]", - [C(@"foo.bar").stringByDeletingPathExtension isEqual: @"foo"] && - [C(@"foo..bar").stringByDeletingPathExtension isEqual: @"foo."] && - [C(@"sdmc:/foo./bar").stringByDeletingPathExtension - isEqual: @"sdmc:/foo./bar"] && - [C(@"sdmc:/foo./bar.baz").stringByDeletingPathExtension - isEqual: @"sdmc:/foo./bar"] && - [C(@"foo.bar/").stringByDeletingPathExtension isEqual: @"foo"] && - [C(@".foo").stringByDeletingPathExtension isEqual: @".foo"] && - [C(@".foo.bar").stringByDeletingPathExtension isEqual: @".foo"]) -# else - TEST(@"-[stringByDeletingPathExtension]", - [C(@"foo.bar").stringByDeletingPathExtension isEqual: @"foo"] && - [C(@"foo..bar").stringByDeletingPathExtension isEqual: @"foo."] && - [C(@"/foo./bar").stringByDeletingPathExtension - isEqual: @"/foo./bar"] && - [C(@"/foo./bar.baz").stringByDeletingPathExtension - isEqual: @"/foo./bar"] && - [C(@"foo.bar/").stringByDeletingPathExtension isEqual: @"foo"] && - [C(@".foo").stringByDeletingPathExtension isEqual: @".foo"] && - [C(@".foo\\bar").stringByDeletingPathExtension - isEqual: @".foo\\bar"] && - [C(@".foo.bar").stringByDeletingPathExtension isEqual: @".foo"]) -# endif - -# ifdef OF_WINDOWS - /* TODO: Add more tests */ - TEST(@"-[stringByStandardizingPath]", - [C(@"\\\\foo\\..\\bar\\qux").stringByStandardizingPath - isEqual: @"\\\\bar\\qux"] && - [C(@"c:\\..\\asd").stringByStandardizingPath - isEqual: @"c:\\..\\asd"]) -# endif -#endif - - TEST(@"-[longLongValue]", - C(@"1234").longLongValue == 1234 && - C(@"\r\n+123 ").longLongValue == 123 && - C(@"-500\t").longLongValue == -500 && - [C(@"-0x10\t") longLongValueWithBase: 0] == -0x10 && - C(@"\t\t\r\n").longLongValue == 0 && - [C(@"123f") longLongValueWithBase: 16] == 0x123f && - [C(@"-1234") longLongValueWithBase: 0] == -1234 && - [C(@"\t\n0xABcd\r") longLongValueWithBase: 0] == 0xABCD && - [C(@"1234567") longLongValueWithBase: 8] == 01234567 && - [C(@"\r\n0123") longLongValueWithBase: 0] == 0123 && - [C(@"765\t") longLongValueWithBase: 8] == 0765 && - [C(@"\t\t\r\n") longLongValueWithBase: 8] == 0) - - TEST(@"-[unsignedLongLongValue]", - C(@"1234").unsignedLongLongValue == 1234 && - C(@"\r\n+123 ").unsignedLongLongValue == 123 && - C(@"\t\t\r\n").unsignedLongLongValue == 0 && - [C(@"123f") unsignedLongLongValueWithBase: 16] == 0x123f && - [C(@"1234") unsignedLongLongValueWithBase: 0] == 1234 && - [C(@"\t\n0xABcd\r") unsignedLongLongValueWithBase: 0] == 0xABCD && - [C(@"1234567") unsignedLongLongValueWithBase: 8] == 01234567 && - [C(@"\r\n0123") unsignedLongLongValueWithBase: 0] == 0123 && - [C(@"765\t") unsignedLongLongValueWithBase: 8] == 0765 && - [C(@"\t\t\r\n") unsignedLongLongValueWithBase: 8] == 0) - - /* - * These test numbers can be generated without rounding if we have IEEE - * floating point numbers, thus we can use == on them. - */ - TEST(@"-[floatValue]", - C(@"\t-0.25 ").floatValue == -0.25 && - C(@"\r\n\tINF\t\n").floatValue == INFINITY && - C(@"\r -INFINITY\n").floatValue == -INFINITY && - isnan(C(@" NAN\t\t").floatValue) && - isnan(C(@" -NaN\t\t").floatValue)) - -#if !defined(OF_ANDROID) && !defined(OF_SOLARIS) && !defined(OF_HPUX) && \ - !defined(OF_DJGPP) && !defined(OF_AMIGAOS_M68K) -# define INPUT @"\t-0x1.FFFFFFFFFFFFFP-1020 " -# define EXPECTED -0x1.FFFFFFFFFFFFFP-1020 -#else -/* Android, Solaris, HP-UX, DJGPP and AmigaOS 3 do not accept 0x for strtod() */ -# if (!defined(OF_SOLARIS) || !defined(OF_X86)) && !defined(OF_AMIGAOS_M68K) -# define INPUT @"\t-0.123456789 " -# define EXPECTED -0.123456789 -# else -/* - * Solaris' strtod() has weird rounding on x86, but not on AMD64. - * AmigaOS 3 with libnix has weird rounding as well. - */ -# define INPUT @"\t-0.125 " -# define EXPECTED -0.125 -# endif -#endif - TEST(@"-[doubleValue]", - INPUT.doubleValue == EXPECTED && - C(@"\r\n\tINF\t\n").doubleValue == INFINITY && - C(@"\r -INFINITY\n").doubleValue == -INFINITY && - isnan(C(@" NAN\t\t").doubleValue)) -#undef INPUT -#undef EXPECTED - - EXPECT_EXCEPTION(@"Detect invalid chars in -[longLongValue] #1", - OFInvalidFormatException, [C(@"abc") longLongValue]) - EXPECT_EXCEPTION(@"Detect invalid chars in -[longLongValue] #2", - OFInvalidFormatException, [C(@"0a") longLongValue]) - EXPECT_EXCEPTION(@"Detect invalid chars in -[longLongValue] #3", - OFInvalidFormatException, [C(@"0 1") longLongValue]) - EXPECT_EXCEPTION(@"Detect invalid chars in -[longLongValue] #4", - OFInvalidFormatException, - [C(@"0xABCDEFG") longLongValueWithBase: 0]) - EXPECT_EXCEPTION(@"Detect invalid chars in -[longLongValue] #5", - OFInvalidFormatException, [C(@"0x") longLongValueWithBase: 0]) - - EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #1", - OFInvalidFormatException, [C(@"0.0a") floatValue]) - EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #2", - OFInvalidFormatException, [C(@"0 0") floatValue]) -#ifdef HAVE_STRTOF_L - /* - * Only do this if we have strtof_l, as the locale might allow the - * comma. - */ - EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #3", - OFInvalidFormatException, [C(@"0,0") floatValue]) -#endif - - EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #1", - OFInvalidFormatException, [C(@"0.0a") doubleValue]) - EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #2", - OFInvalidFormatException, [C(@"0 0") doubleValue]) -#ifdef HAVE_STRTOD_L - /* - * Only do this if we have strtod_l, as the locale might allow the - * comma. - */ - EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #3", - OFInvalidFormatException, [C(@"0,0") doubleValue]) -#endif - - EXPECT_EXCEPTION(@"Detect out of range in -[longLongValue]", - OFOutOfRangeException, - [C(@"-12345678901234567890123456789012345678901234567890" - @"12345678901234567890123456789012345678901234567890") - longLongValueWithBase: 16]) - - EXPECT_EXCEPTION(@"Detect out of range in -[unsignedLongLongValue]", - OFOutOfRangeException, - [C(@"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" - @"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF") - unsignedLongLongValueWithBase: 16]) - - TEST(@"-[characters]", (characters = C(@"fööbär🀺").characters) && - !memcmp(characters, unicharString + 1, sizeof(unicharString) - 8)) - -#ifdef OF_BIG_ENDIAN -# define swappedByteOrder OFByteOrderLittleEndian -#else -# define swappedByteOrder OFByteOrderBigEndian -#endif - TEST(@"-[UTF16String]", (UTF16Characters = C(@"fööbär🀺").UTF16String) && - !memcmp(UTF16Characters, char16String + 1, - OFUTF16StringLength(char16String) * 2) && - (UTF16Characters = [C(@"fööbär🀺") - UTF16StringWithByteOrder: swappedByteOrder]) && - !memcmp(UTF16Characters, swappedChar16String + 1, - OFUTF16StringLength(swappedChar16String) * 2)) - - TEST(@"-[UTF16StringLength]", C(@"fööbär🀺").UTF16StringLength == 8) - - TEST(@"-[UTF32String]", (characters = C(@"fööbär🀺").UTF32String) && - !memcmp(characters, unicharString + 1, - OFUTF32StringLength(unicharString) * 4) && - (characters = [C(@"fööbär🀺") UTF32StringWithByteOrder: - swappedByteOrder]) && - !memcmp(characters, swappedUnicharString + 1, - OFUTF32StringLength(swappedUnicharString) * 4)) -#undef swappedByteOrder - - TEST(@"-[stringByMD5Hashing]", [C(@"asdfoobar").stringByMD5Hashing - isEqual: @"184dce2ec49b5422c7cfd8728864db4c"]) - - TEST(@"-[stringByRIPEMD160Hashing]", - [C(@"asdfoobar").stringByRIPEMD160Hashing - isEqual: @"021d773b0fac06eb6755ca6aa58a580c980f7f13"]) - - TEST(@"-[stringBySHA1Hashing]", [C(@"asdfoobar").stringBySHA1Hashing - isEqual: @"f5f81ac0a8b5cbfdc4585ec1ad32e7b3a12b9b49"]) - - TEST(@"-[stringBySHA224Hashing]", [C(@"asdfoobar").stringBySHA224Hashing - isEqual: @"5a06822dcbd5a874f67d062b80b9d8a9cb9b5b303960b9da9290c192" - ]) - - TEST(@"-[stringBySHA256Hashing]", [C(@"asdfoobar").stringBySHA256Hashing - isEqual: @"28e65b1dcd7f6ce2ea6277b15f87b913628b5500bf7913a2bbf4cedc" - @"fa1215f6"]) - - TEST(@"-[stringBySHA384Hashing]", [C(@"asdfoobar").stringBySHA384Hashing - isEqual: @"73286da882ffddca2f45e005cfa6b44f3fc65bfb26db1d087ded2f9c" - @"279e5addf8be854044bca0cece073fce28eec7d9"]) - - TEST(@"-[stringBySHA512Hashing]", [C(@"asdfoobar").stringBySHA512Hashing - isEqual: @"0464c427da158b02161bb44a3090bbfc594611ef6a53603640454b56" - @"412a9247c3579a329e53a5dc74676b106755e3394f9454a2d4227324" - @"2615d32f80437d61"]) - - characterSet = - [OFCharacterSet characterSetWithCharactersInString: @"abfo'_~$🍏"]; - TEST(@"-[stringByAddingPercentEncodingWithAllowedCharacters:]", - [[C(@"foo\"ba'_~$]🍏🍌") - stringByAddingPercentEncodingWithAllowedCharacters: characterSet] - isEqual: @"foo%22ba'_~$%5D🍏%F0%9F%8D%8C"]) - - TEST(@"-[stringByRemovingPercentEncoding]", - [C(@"foo%20bar%22+%24%F0%9F%8D%8C").stringByRemovingPercentEncoding - isEqual: @"foo bar\"+$🍌"]) - - TEST(@"-[insertString:atIndex:]", - (mutableString1 = [mutableStringClass - stringWithString: @"𝄞öööbä€"]) && - R([mutableString1 insertString: @"äöü" atIndex: 3]) && - [mutableString1 isEqual: @"𝄞ööäöüöbä€"]) - - EXPECT_EXCEPTION(@"Detect invalid format in " - @"-[stringByRemovingPercentEncoding] #1", - OFInvalidFormatException, - [C(@"foo%xbar") stringByRemovingPercentEncoding]) - EXPECT_EXCEPTION(@"Detect invalid encoding in " - @"-[stringByRemovingPercentEncoding] #2", - OFInvalidEncodingException, - [C(@"foo%FFbar") stringByRemovingPercentEncoding]) - - TEST(@"-[setCharacter:atIndex:]", - (mutableString1 = [mutableStringClass - stringWithString: @"abäde"]) && - R([mutableString1 setCharacter: 0xF6 atIndex: 2]) && - [mutableString1 isEqual: @"aböde"] && - R([mutableString1 setCharacter: 'c' atIndex: 2]) && - [mutableString1 isEqual: @"abcde"] && - R([mutableString1 setCharacter: 0x20AC atIndex: 3]) && - [mutableString1 isEqual: @"abc€e"] && - R([mutableString1 setCharacter: 'x' atIndex: 1]) && - [mutableString1 isEqual: @"axc€e"]) - - TEST(@"-[deleteCharactersInRange:]", - (mutableString1 = [mutableStringClass - stringWithString: @"𝄞öööbä€"]) && - R([mutableString1 deleteCharactersInRange: OFMakeRange(1, 3)]) && - [mutableString1 isEqual: @"𝄞bä€"] && - R([mutableString1 deleteCharactersInRange: OFMakeRange(0, 4)]) && - [mutableString1 isEqual: @""]) - - TEST(@"-[replaceCharactersInRange:withString:]", - (mutableString1 = [mutableStringClass - stringWithString: @"𝄞öööbä€"]) && - R([mutableString1 replaceCharactersInRange: OFMakeRange(1, 3) - withString: @"äöüß"]) && - [mutableString1 isEqual: @"𝄞äöüßbä€"] && - R([mutableString1 replaceCharactersInRange: OFMakeRange(4, 2) - withString: @"b"]) && - [mutableString1 isEqual: @"𝄞äöübä€"] && - R([mutableString1 replaceCharactersInRange: OFMakeRange(0, 7) - withString: @""]) && - [mutableString1 isEqual: @""]) - - EXPECT_EXCEPTION(@"Detect OoR in -[deleteCharactersInRange:] #1", - OFOutOfRangeException, - { - mutableString1 = [mutableStringClass stringWithString: @"𝄞öö"]; - [mutableString1 deleteCharactersInRange: OFMakeRange(2, 2)]; - }) - - EXPECT_EXCEPTION(@"Detect OoR in -[deleteCharactersInRange:] #2", - OFOutOfRangeException, - [mutableString1 deleteCharactersInRange: OFMakeRange(4, 0)]) - - EXPECT_EXCEPTION(@"Detect OoR in " - @"-[replaceCharactersInRange:withString:] #1", - OFOutOfRangeException, - [mutableString1 replaceCharactersInRange: OFMakeRange(2, 2) - withString: @""]) - - EXPECT_EXCEPTION(@"Detect OoR in " - @"-[replaceCharactersInRange:withString:] #2", - OFOutOfRangeException, - [mutableString1 replaceCharactersInRange: OFMakeRange(4, 0) - withString: @""]) - - TEST(@"-[replaceOccurrencesOfString:withString:]", - (mutableString1 = [mutableStringClass stringWithString: - @"asd fo asd fofo asd"]) && - R([mutableString1 replaceOccurrencesOfString: @"fo" - withString: @"foo"]) && - [mutableString1 isEqual: @"asd foo asd foofoo asd"] && - (mutableString1 = [mutableStringClass stringWithString: @"XX"]) && - R([mutableString1 replaceOccurrencesOfString: @"X" - withString: @"XX"]) && - [mutableString1 isEqual: @"XXXX"]) - - TEST(@"-[replaceOccurrencesOfString:withString:options:range:]", - (mutableString1 = [mutableStringClass stringWithString: - @"foofoobarfoobarfoo"]) && R([mutableString1 - replaceOccurrencesOfString: @"oo" - withString: @"óò" - options: 0 - range: OFMakeRange(2, 15)]) && - [mutableString1 isEqual: @"foofóòbarfóòbarfoo"]) - - TEST(@"-[deleteLeadingWhitespaces]", - (mutableString1 = [mutableStringClass - stringWithString: whitespace[0]]) && - R([mutableString1 deleteLeadingWhitespaces]) && - [mutableString1 isEqual: @"asd \t \t\t\r\n"] && - (mutableString1 = [mutableStringClass - stringWithString: whitespace[1]]) && - R([mutableString1 deleteLeadingWhitespaces]) && - [mutableString1 isEqual: @""]) - - TEST(@"-[deleteTrailingWhitespaces]", - (mutableString1 = [mutableStringClass - stringWithString: whitespace[0]]) && - R([mutableString1 deleteTrailingWhitespaces]) && - [mutableString1 isEqual: @" \r \t\n\t \tasd"] && - (mutableString1 = [mutableStringClass - stringWithString: whitespace[1]]) && - R([mutableString1 deleteTrailingWhitespaces]) && - [mutableString1 isEqual: @""]) - - TEST(@"-[deleteEnclosingWhitespaces]", - (mutableString1 = [mutableStringClass - stringWithString: whitespace[0]]) && - R([mutableString1 deleteEnclosingWhitespaces]) && - [mutableString1 isEqual: @"asd"] && - (mutableString1 = [mutableStringClass - stringWithString: whitespace[1]]) && - R([mutableString1 deleteEnclosingWhitespaces]) && - [mutableString1 isEqual: @""]) - - TEST(@"-[stringByXMLEscaping]", - (string = C(@" &world'\"!&").stringByXMLEscaping) && - [string isEqual: @"<hello> &world'"!&"]) - - TEST(@"-[stringByXMLUnescaping]", - [string.stringByXMLUnescaping isEqual: @" &world'\"!&"] && - [C(@"y").stringByXMLUnescaping isEqual: @"y"] && - [C(@"ä").stringByXMLUnescaping isEqual: @"ä"] && - [C(@"€").stringByXMLUnescaping isEqual: @"€"] && - [C(@"𝄞").stringByXMLUnescaping isEqual: @"𝄞"]) - - EXPECT_EXCEPTION(@"Detect unknown entities in -[stringByXMLUnescaping]", - OFUnknownXMLEntityException, [C(@"&foo;") stringByXMLUnescaping]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#1", OFInvalidFormatException, - [C(@"x&") stringByXMLUnescaping]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#2", OFInvalidFormatException, [C(@"&#;") stringByXMLUnescaping]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#3", OFInvalidFormatException, [C(@"&#x;") stringByXMLUnescaping]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#4", OFInvalidFormatException, [C(@"&#g;") stringByXMLUnescaping]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#5", OFInvalidFormatException, - [C(@"&#xg;") stringByXMLUnescaping]) - - TEST(@"-[stringByXMLUnescapingWithDelegate:]", - (entityHandler = [[[EntityHandler alloc] init] autorelease]) && - [[C(@"x&foo;y") stringByXMLUnescapingWithDelegate: entityHandler] - isEqual: @"xbary"]) - -#ifdef OF_HAVE_BLOCKS - TEST(@"-[stringByXMLUnescapingWithBlock:]", - [[C(@"x&foo;y") stringByXMLUnescapingWithBlock: - ^ OFString *(OFString *str, OFString *entity) { - if ([entity isEqual: @"foo"]) - return @"bar"; - - return nil; - }] isEqual: @"xbary"]) - - j = 0; - ok = true; - [C(@"foo\nbar\nbaz") enumerateLinesUsingBlock: - ^ (OFString *line, bool *stop) { - switch (j) { - case 0: - if (![line isEqual: @"foo"]) - ok = false; - break; - case 1: - if (![line isEqual: @"bar"]) - ok = false; - break; - case 2: - if (![line isEqual: @"baz"]) - ok = false; - break; - default: - ok = false; - } - - j++; - }]; - TEST(@"-[enumerateLinesUsingBlock:]", ok) -#endif - -#undef C - - objc_autoreleasePoolPop(pool); -} - -- (void)stringTests -{ - module = @"OFString"; - [self stringTestsWithClass: [SimpleString class] - mutableClass: [SimpleMutableString class]]; - - module = @"OFString_UTF8"; - [self stringTestsWithClass: [OFUTF8String class] - mutableClass: [OFMutableUTF8String class]]; -} @end Index: tests/OFSubprocessTests.m ================================================================== --- tests/OFSubprocessTests.m +++ tests/OFSubprocessTests.m @@ -13,18 +13,19 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFSubprocess"; - -@implementation TestsAppDelegate (OFSubprocessTests) -- (void)subprocessTests -{ - void *pool = objc_autoreleasePoolPush(); +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFSubprocessTests: OTTestCase +@end + +@implementation OFSubprocessTests +- (void)testSubprocess +{ #ifdef OF_HAVE_FILES OFString *program = [@"subprocess" stringByAppendingPathComponent: @"subprocess" @PROG_SUFFIX]; #else OFString *program = @"subprocess/subprocess" @PROG_SUFFIX; @@ -34,24 +35,22 @@ [[[OFApplication environment] mutableCopy] autorelease]; OFSubprocess *subprocess; [environment setObject: @"yés" forKey: @"tëst"]; - TEST(@"+[subprocessWithProgram:programName:arguments:environment]", - (subprocess = - [OFSubprocess subprocessWithProgram: program - programName: program - arguments: arguments - environment: environment])) - - TEST(@"Standard input", R([subprocess writeLine: @"Hellö world!"])) - - TEST(@"Standard output", - [[subprocess readLine] isEqual: @"HELLÖ WORLD!"]) - - TEST(@"-[closeForWriting]", R([subprocess closeForWriting])) - - TEST(@"-[waitForTermination]", [subprocess waitForTermination] == 0) - - objc_autoreleasePoolPop(pool); + subprocess = [OFSubprocess subprocessWithProgram: program + programName: program + arguments: arguments + environment: environment]; + + [subprocess writeLine: @"Hellö world!"]; +#ifdef OF_HAVE_UNICODE_TABLES + OTAssertEqualObjects([subprocess readLine], @"HELLÖ WORLD!"); +#else + OTAssertEqualObjects([subprocess readLine], @"HELLö WORLD!"); +#endif + + [subprocess closeForWriting]; + + OTAssertEqual([subprocess waitForTermination], 0); } @end Index: tests/OFSystemInfoTests.m ================================================================== --- tests/OFSystemInfoTests.m +++ tests/OFSystemInfoTests.m @@ -13,228 +13,162 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFSystemInfoTests: OTTestCase +@end #ifdef OF_HAVE_SOCKETS static void -printAddresses(OFData *addresses, bool *firstAddress) +appendAddresses(OFMutableString *string, OFData *addresses, bool *firstAddress) { size_t count = addresses.count; for (size_t i = 0; i < count; i++) { const OFSocketAddress *address = [addresses itemAtIndex: i]; if (!*firstAddress) - [OFStdOut writeString: @", "]; - - *firstAddress = false; - - [OFStdOut writeString: OFSocketAddressString(address)]; - } -} -#endif - -@implementation TestsAppDelegate (OFSystemInfoTests) -- (void)systemInfoTests -{ - void *pool = objc_autoreleasePoolPush(); -#ifdef OF_HAVE_SOCKETS - OFDictionary *networkInterfaces; - bool firstInterface = true; -#endif - - [OFStdOut setForegroundColor: [OFColor lime]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Page size: %zd\n", - [OFSystemInfo pageSize]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Number of CPUs: %zd\n", - [OFSystemInfo numberOfCPUs]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] ObjFW version: %@\n", - [OFSystemInfo ObjFWVersion]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] ObjFW version major: %u\n", - [OFSystemInfo ObjFWVersionMajor]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] ObjFW version minor: %u\n", - [OFSystemInfo ObjFWVersionMinor]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Operating system name: %@\n", - [OFSystemInfo operatingSystemName]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Operating system version: %@\n", - [OFSystemInfo operatingSystemVersion]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] User config IRI: %@\n", - [OFSystemInfo userConfigIRI].string]; - - [OFStdOut writeFormat: @"[OFSystemInfo] User data IRI: %@\n", - [OFSystemInfo userDataIRI].string]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Temporary directory IRI: %@\n", - [OFSystemInfo temporaryDirectoryIRI].string]; - - [OFStdOut writeFormat: @"[OFSystemInfo] CPU vendor: %@\n", - [OFSystemInfo CPUVendor]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] CPU model: %@\n", - [OFSystemInfo CPUModel]]; - -#if defined(OF_AMD64) || defined(OF_X86) - [OFStdOut writeFormat: @"[OFSystemInfo] Supports MMX: %d\n", - [OFSystemInfo supportsMMX]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports 3DNow!: %d\n", - [OFSystemInfo supports3DNow]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports enhanced 3DNow!: %d\n", - [OFSystemInfo supportsEnhanced3DNow]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE: %d\n", - [OFSystemInfo supportsSSE]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE2: %d\n", - [OFSystemInfo supportsSSE2]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE3: %d\n", - [OFSystemInfo supportsSSE3]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports SSSE3: %d\n", - [OFSystemInfo supportsSSSE3]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE4.1: %d\n", - [OFSystemInfo supportsSSE41]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE4.2: %d\n", - [OFSystemInfo supportsSSE42]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports AVX: %d\n", - [OFSystemInfo supportsAVX]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports AVX2: %d\n", - [OFSystemInfo supportsAVX2]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports AES-NI: %d\n", - [OFSystemInfo supportsAESNI]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports SHA extensions: %d\n", - [OFSystemInfo supportsSHAExtensions]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports fused multiply-add: " - @"%d\n", - [OFSystemInfo supportsFusedMultiplyAdd]]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Supports F16C: %d\n", - [OFSystemInfo supportsF16C]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Foundation: %d\n", - [OFSystemInfo supportsAVX512Foundation]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Conflict Detection Instructions: " - @"%d\n", - [OFSystemInfo supportsAVX512ConflictDetectionInstructions]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Exponential and Reciprocal " - @"Instructions: %d\n", - [OFSystemInfo supportsAVX512ExponentialAndReciprocalInstructions]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Prefetch Instructions: %d\n", - [OFSystemInfo supportsAVX512PrefetchInstructions]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Vector Length Extensions: %d\n", - [OFSystemInfo supportsAVX512VectorLengthExtensions]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Doubleword and Quadword " - @"Instructions: %d\n", - [OFSystemInfo supportsAVX512DoublewordAndQuadwordInstructions]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Byte and Word Instructions: %d\n", - [OFSystemInfo supportsAVX512ByteAndWordInstructions]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Integer Fused Multiply Add: %d\n", - [OFSystemInfo supportsAVX512IntegerFusedMultiplyAdd]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Vector Byte Manipulation " - @"Instructions: %d\n", - [OFSystemInfo supportsAVX512VectorByteManipulationInstructions]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Vector Population Count " - @"Instruction: %d\n", - [OFSystemInfo supportsAVX512VectorPopulationCountInstruction]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Vector Neutral Network " - @"Instructions: %d\n", - [OFSystemInfo supportsAVX512VectorNeuralNetworkInstructions]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Vector Byte Manipulation " - @"Instructions 2: %d\n", - [OFSystemInfo supportsAVX512VectorByteManipulationInstructions2]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Bit Algorithms: %d\n", - [OFSystemInfo supportsAVX512BitAlgorithms]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 Float16 Instructions: %d\n", - [OFSystemInfo supportsAVX512Float16Instructions]]; - - [OFStdOut writeFormat: - @"[OFSystemInfo] Supports AVX-512 BFloat16 Instructions: %d\n", - [OFSystemInfo supportsAVX512BFloat16Instructions]]; -#endif - -#ifdef OF_POWERPC - [OFStdOut writeFormat: @"[OFSystemInfo] Supports AltiVec: %d\n", - [OFSystemInfo supportsAltiVec]]; -#endif - -#ifdef OF_HAVE_SOCKETS - networkInterfaces = [OFSystemInfo networkInterfaces]; - [OFStdOut writeString: @"[OFSystemInfo] Network interfaces: "]; + [string appendString: @", "]; + + *firstAddress = false; + + [string appendString: OFSocketAddressString(address)]; + } +} +#endif + +@implementation OFSystemInfoTests ++ (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, id) *) *)summary +{ + OFMutableArray *summary = [OFMutableArray array]; +#ifdef OF_HAVE_SOCKETS + OFDictionary *networkInterfaces; + OFMutableString *networkInterfacesString; + bool firstInterface = true; +#endif + +#define ADD(name, value) \ + [summary addObject: [OFPair pairWithFirstObject: name \ + secondObject: value]]; +#define ADD_UINT(name, value) \ + ADD(name, [OFNumber numberWithUnsignedInt: value]); +#define ADD_ULONGLONG(name, value) \ + ADD(name, [OFNumber numberWithUnsignedLongLong: value]); +#define ADD_BOOL(name, value) \ + ADD(name, [OFNumber numberWithBool: value]); + + ADD(@"ObjFW version", [OFSystemInfo ObjFWVersion]) + ADD_UINT(@"ObjFW version major", [OFSystemInfo ObjFWVersionMajor]) + ADD_UINT(@"ObjFW version minor", [OFSystemInfo ObjFWVersionMinor]) + ADD(@"Operating system name", [OFSystemInfo operatingSystemName]); + ADD(@"Operating system version", [OFSystemInfo operatingSystemVersion]); + ADD_ULONGLONG(@"Page size", [OFSystemInfo pageSize]); + ADD_ULONGLONG(@"Number of CPUs", [OFSystemInfo numberOfCPUs]); + ADD(@"User config IRI", [OFSystemInfo userConfigIRI].string); + ADD(@"User data IRI", [OFSystemInfo userDataIRI].string); + ADD(@"Temporary directory IRI", + [OFSystemInfo temporaryDirectoryIRI].string); + ADD(@"CPU vendor", [OFSystemInfo CPUVendor]); + ADD(@"CPU model", [OFSystemInfo CPUModel]); + +#if defined(OF_AMD64) || defined(OF_X86) + ADD_BOOL(@"Supports MMX", [OFSystemInfo supportsMMX]); + ADD_BOOL(@"Supports 3DNow!", [OFSystemInfo supports3DNow]); + ADD_BOOL(@"Supports enhanced 3DNow!", + [OFSystemInfo supportsEnhanced3DNow]); + ADD_BOOL(@"Supports SSE", [OFSystemInfo supportsSSE]); + ADD_BOOL(@"Supports SSE2", [OFSystemInfo supportsSSE2]); + ADD_BOOL(@"Supports SSE3", [OFSystemInfo supportsSSE3]); + ADD_BOOL(@"Supports SSSE3", [OFSystemInfo supportsSSSE3]); + ADD_BOOL(@"Supports SSE4.1", [OFSystemInfo supportsSSE41]); + ADD_BOOL(@"Supports SSE4.2", [OFSystemInfo supportsSSE42]); + ADD_BOOL(@"Supports AVX", [OFSystemInfo supportsAVX]); + ADD_BOOL(@"Supports AVX2", [OFSystemInfo supportsAVX2]); + ADD_BOOL(@"Supports AES-NI", [OFSystemInfo supportsAESNI]); + ADD_BOOL(@"Supports SHA extensions", + [OFSystemInfo supportsSHAExtensions]); + ADD_BOOL(@"Supports fused multiply-add", + [OFSystemInfo supportsFusedMultiplyAdd]); + ADD_BOOL(@"Supports F16C", [OFSystemInfo supportsF16C]); + ADD_BOOL(@"Supports AVX-512 Foundation", + [OFSystemInfo supportsAVX512Foundation]); + ADD_BOOL(@"Supports AVX-512 Conflict Detection Instructions", + [OFSystemInfo supportsAVX512ConflictDetectionInstructions]); + ADD_BOOL(@"Supports AVX-512 Exponential and Reciprocal Instructions", + [OFSystemInfo supportsAVX512ExponentialAndReciprocalInstructions]); + ADD_BOOL(@"Supports AVX-512 Prefetch Instructions", + [OFSystemInfo supportsAVX512PrefetchInstructions]); + ADD_BOOL(@"Supports AVX-512 Vector Length Extensions", + [OFSystemInfo supportsAVX512VectorLengthExtensions]); + ADD_BOOL(@"Supports AVX-512 Doubleword and Quadword Instructions", + [OFSystemInfo supportsAVX512DoublewordAndQuadwordInstructions]); + ADD_BOOL(@"Supports AVX-512 Byte and Word Instructions", + [OFSystemInfo supportsAVX512ByteAndWordInstructions]); + ADD_BOOL(@"Supports AVX-512 Integer Fused Multiply Add", + [OFSystemInfo supportsAVX512IntegerFusedMultiplyAdd]); + ADD_BOOL(@"Supports AVX-512 Vector Byte Manipulation Instructions", + [OFSystemInfo supportsAVX512VectorByteManipulationInstructions]); + ADD_BOOL(@"Supports AVX-512 Vector Population Count Instruction", + [OFSystemInfo supportsAVX512VectorPopulationCountInstruction]); + ADD_BOOL(@"Supports AVX-512 Vector Neutral Network Instructions", + [OFSystemInfo supportsAVX512VectorNeuralNetworkInstructions]); + ADD_BOOL(@"Supports AVX-512 Vector Byte Manipulation Instructions 2", + [OFSystemInfo supportsAVX512VectorByteManipulationInstructions2]); + ADD_BOOL(@"Supports AVX-512 Bit Algorithms", + [OFSystemInfo supportsAVX512BitAlgorithms]); + ADD_BOOL(@"Supports AVX-512 Float16 Instructions", + [OFSystemInfo supportsAVX512Float16Instructions]); + ADD_BOOL(@"Supports AVX-512 BFloat16 Instructions", + [OFSystemInfo supportsAVX512BFloat16Instructions]); +#endif + +#ifdef OF_POWERPC + ADD_BOOL(@"Supports AltiVec", [OFSystemInfo supportsAltiVec]); +#endif + +#undef ADD +#undef ADD_UINT +#undef ADD_ULONGLONG +#undef ADD_BOOL + +#ifdef OF_HAVE_SOCKETS + networkInterfaces = [OFSystemInfo networkInterfaces]; + networkInterfacesString = [OFMutableString string]; for (OFString *name in networkInterfaces) { bool firstAddress = true; OFNetworkInterface interface; OFData *hardwareAddress; if (!firstInterface) - [OFStdOut writeString: @"; "]; + [networkInterfacesString appendString: @"; "]; firstInterface = false; - [OFStdOut writeFormat: @"%@(", name]; + [networkInterfacesString appendFormat: @"%@(", name]; interface = [networkInterfaces objectForKey: name]; - printAddresses([interface objectForKey: - OFNetworkInterfaceIPv4Addresses], &firstAddress); + appendAddresses(networkInterfacesString, + [interface objectForKey: OFNetworkInterfaceIPv4Addresses], + &firstAddress); # ifdef OF_HAVE_IPV6 - printAddresses([interface objectForKey: - OFNetworkInterfaceIPv6Addresses], &firstAddress); + appendAddresses(networkInterfacesString, + [interface objectForKey: OFNetworkInterfaceIPv6Addresses], + &firstAddress); # endif # ifdef OF_HAVE_IPX - printAddresses([interface objectForKey: - OFNetworkInterfaceIPXAddresses], &firstAddress); + appendAddresses(networkInterfacesString, + [interface objectForKey: OFNetworkInterfaceIPXAddresses], + &firstAddress); # endif # ifdef OF_HAVE_APPLETALK - printAddresses([interface objectForKey: + appendAddresses(networkInterfacesString, + [interface objectForKey: OFNetworkInterfaceAppleTalkAddresses], &firstAddress); # endif hardwareAddress = [interface objectForKey: OFNetworkInterfaceHardwareAddress]; @@ -241,23 +175,27 @@ if (hardwareAddress != nil) { const unsigned char *bytes = hardwareAddress.items; size_t length = hardwareAddress.count; if (!firstAddress) - [OFStdOut writeString: @", "]; + [networkInterfacesString appendString: @", "]; for (size_t i = 0; i < length; i++) { if (i > 0) - [OFStdOut writeString: @":"]; + [networkInterfacesString + appendString: @":"]; - [OFStdOut writeFormat: @"%02X", bytes[i]]; + [networkInterfacesString + appendFormat: @"%02X", bytes[i]]; } } - [OFStdOut writeString: @")"]; + [networkInterfacesString appendString: @")"]; } - [OFStdOut writeString: @"\n"]; + [summary addObject: + [OFPair pairWithFirstObject: @"Network interfaces" + secondObject: networkInterfacesString]]; #endif - objc_autoreleasePoolPop(pool); + return summary; } @end Index: tests/OFTCPSocketTests.m ================================================================== --- tests/OFTCPSocketTests.m +++ tests/OFTCPSocketTests.m @@ -15,44 +15,37 @@ #include "config.h" #include -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFTCPSocket"; - -@implementation TestsAppDelegate (OFTCPSocketTests) -- (void)TCPSocketTests -{ - void *pool = objc_autoreleasePoolPush(); - OFTCPSocket *server, *client = nil, *accepted; +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFTCPSocketTests: OTTestCase +@end + +@implementation OFTCPSocketTests +- (void)testTCPSocket +{ + OFTCPSocket *server, *client, *accepted; OFSocketAddress address; char buffer[6]; - TEST(@"+[socket]", (server = [OFTCPSocket socket]) && - (client = [OFTCPSocket socket])) - - TEST(@"-[bindToHost:port:]", - R(address = [server bindToHost: @"127.0.0.1" port: 0])) - - TEST(@"-[listen]", R([server listen])) - - TEST(@"-[connectToHost:port:]", - R([client connectToHost: @"127.0.0.1" - port: OFSocketAddressIPPort(&address)])) - - TEST(@"-[accept]", (accepted = [server accept])) - - TEST(@"-[remoteAddress]", - [OFSocketAddressString(accepted.remoteAddress) - isEqual: @"127.0.0.1"]) - - TEST(@"-[writeString:]", R([client writeString: @"Hello!"])) - - TEST(@"-[readIntoBuffer:length:]", - [accepted readIntoBuffer: buffer length: 6] && - !memcmp(buffer, "Hello!", 6)) - - objc_autoreleasePoolPop(pool); + server = [OFTCPSocket socket]; + client = [OFTCPSocket socket]; + + address = [server bindToHost: @"127.0.0.1" port: 0]; + [server listen]; + + [client connectToHost: @"127.0.0.1" + port: OFSocketAddressIPPort(&address)]; + + accepted = [server accept]; + OTAssertEqualObjects(OFSocketAddressString(accepted.remoteAddress), + @"127.0.0.1"); + + [client writeString: @"Hello!"]; + + [accepted readIntoBuffer: buffer exactLength: 6]; + OTAssertEqual(memcmp(buffer, "Hello!", 6), 0); } @end Index: tests/OFThreadTests.m ================================================================== --- tests/OFThreadTests.m +++ tests/OFThreadTests.m @@ -13,13 +13,15 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFThread"; +@interface OFThreadTests: OTTestCase +@end @interface TestThread: OFThread @end @implementation TestThread @@ -31,23 +33,16 @@ return @"success"; } @end -@implementation TestsAppDelegate (OFThreadTests) -- (void)threadTests -{ - void *pool = objc_autoreleasePoolPush(); - TestThread *thread; - - TEST(@"+[thread]", (thread = [TestThread thread])) - - TEST(@"-[start]", R([thread start])) - - TEST(@"-[join]", [[thread join] isEqual: @"success"]) - - TEST(@"-[threadDictionary]", - [[OFThread threadDictionary] objectForKey: @"foo"] == nil) - - objc_autoreleasePoolPop(pool); +@implementation OFThreadTests +- (void)testThread +{ + TestThread *thread = [TestThread thread]; + + [thread start]; + + OTAssertEqualObjects([thread join], @"success"); + OTAssertNil([[OFThread threadDictionary] objectForKey: @"foo"]); } @end Index: tests/OFUDPSocketTests.m ================================================================== --- tests/OFUDPSocketTests.m +++ tests/OFUDPSocketTests.m @@ -15,45 +15,32 @@ #include "config.h" #include -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFUDPSocket"; - -@implementation TestsAppDelegate (OFUDPSocketTests) -- (void)UDPSocketTests -{ - void *pool = objc_autoreleasePoolPush(); - OFUDPSocket *sock; - OFSocketAddress addr1, addr2, addr3; - char buf[6]; - - TEST(@"+[socket]", (sock = [OFUDPSocket socket])) - - TEST(@"-[bindToHost:port:]", - R(addr1 = [sock bindToHost: @"127.0.0.1" port: 0])) - - TEST(@"-[sendBuffer:length:receiver:]", - R([sock sendBuffer: "Hello" length: 6 receiver: &addr1])) - - TEST(@"-[receiveIntoBuffer:length:sender:]", - [sock receiveIntoBuffer: buf length: 6 sender: &addr2] == 6 && - !memcmp(buf, "Hello", 6) && - [OFSocketAddressString(&addr2) isEqual: @"127.0.0.1"] && - OFSocketAddressIPPort(&addr2) == OFSocketAddressIPPort(&addr1)) - - addr3 = OFSocketAddressParseIP(@"127.0.0.1", - OFSocketAddressIPPort(&addr1) + 1); - - TEST(@"OFSocketAddressEqual()", - OFSocketAddressEqual(&addr1, &addr2) && - !OFSocketAddressEqual(&addr1, &addr3)) - - TEST(@"OFSocketAddressHash()", - OFSocketAddressHash(&addr1) == OFSocketAddressHash(&addr2) && - OFSocketAddressHash(&addr1) != OFSocketAddressHash(&addr3)) - - objc_autoreleasePoolPop(pool); +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFUDPSocketTests: OTTestCase +@end + +@implementation OFUDPSocketTests +- (void)testUDPSocket +{ + OFUDPSocket *sock = [OFUDPSocket socket]; + OFSocketAddress addr1, addr2; + char buffer[6]; + + sock = [OFUDPSocket socket]; + + addr1 = [sock bindToHost: @"127.0.0.1" port: 0]; + OTAssertEqualObjects(OFSocketAddressString(&addr1), @"127.0.0.1"); + + [sock sendBuffer: "Hello" length: 6 receiver: &addr1]; + + [sock receiveIntoBuffer: buffer length: 6 sender: &addr2]; + OTAssertEqual(memcmp(buffer, "Hello", 6), 0); + OTAssertEqualObjects(OFSocketAddressString(&addr2), @"127.0.0.1"); + OTAssertEqual(OFSocketAddressIPPort(&addr2), + OFSocketAddressIPPort(&addr1)); } @end Index: tests/OFUNIXDatagramSocketTests.m ================================================================== --- tests/OFUNIXDatagramSocketTests.m +++ tests/OFUNIXDatagramSocketTests.m @@ -14,21 +14,23 @@ */ #include "config.h" #include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFUNIXDatagramSocket"; - -@implementation TestsAppDelegate (OFUNIXDatagramSocketTests) -- (void)UNIXDatagramSocketTests -{ - void *pool = objc_autoreleasePoolPush(); - OFString *path; - OFUNIXDatagramSocket *sock; +#include + +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFUNIXDatagramSocketTests: OTTestCase +@end + +@implementation OFUNIXDatagramSocketTests +- (void)testUNIXDatagramSocket +{ + OFUNIXDatagramSocket *sock = [OFUNIXDatagramSocket socket]; + OFString *path; OFSocketAddress address1, address2; char buffer[5]; #if defined(OF_HAVE_FILES) && !defined(OF_IOS) path = [[OFSystemInfo temporaryDirectoryIRI] @@ -44,47 +46,34 @@ */ path = [OFString stringWithFormat: @"/tmp/%@", [[OFUUID UUID] UUIDString]]; #endif - TEST(@"+[socket]", (sock = [OFUNIXDatagramSocket socket])) - @try { - TEST(@"-[bindToPath:]", R(address1 = [sock bindToPath: path])) + address1 = [sock bindToPath: path]; } @catch (OFBindSocketFailedException *e) { switch (e.errNo) { case EAFNOSUPPORT: case EPERM: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFUNIXDatagramSocket] -[bindToPath:]: " - @"UNIX datagram sockets unsupported, skipping " - @"tests"]; - - objc_autoreleasePoolPop(pool); - return; + OTSkip(@"UNIX datagram sockets unsupported"); default: @throw e; } } @try { - TEST(@"-[sendBuffer:length:receiver:]", - R([sock sendBuffer: "Hello" length: 5 receiver: &address1])) - - TEST(@"-[receiveIntoBuffer:length:sender:]", - [sock receiveIntoBuffer: buffer - length: 5 - sender: &address2] == 5 && - memcmp(buffer, "Hello", 5) == 0 && - OFSocketAddressEqual(&address1, &address2) && - OFSocketAddressHash(&address1) == - OFSocketAddressHash(&address2)) + [sock sendBuffer: "Hello" length: 5 receiver: &address1]; + + OTAssertEqual([sock receiveIntoBuffer: buffer + length: 5 + sender: &address2], 5); + OTAssertEqual(memcmp(buffer, "Hello", 5), 0); + OTAssertTrue(OFSocketAddressEqual(&address1, &address2)); + OTAssertEqual(OFSocketAddressHash(&address1), + OFSocketAddressHash(&address2)); } @finally { #ifdef OF_HAVE_FILES [[OFFileManager defaultManager] removeItemAtPath: path]; #endif } - - objc_autoreleasePoolPop(pool); } @end Index: tests/OFUNIXStreamSocketTests.m ================================================================== --- tests/OFUNIXStreamSocketTests.m +++ tests/OFUNIXStreamSocketTests.m @@ -14,19 +14,21 @@ */ #include "config.h" #include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFUNIXStreamSocket"; - -@implementation TestsAppDelegate (OFUNIXStreamSocketTests) -- (void)UNIXStreamSocketTests -{ - void *pool = objc_autoreleasePoolPush(); +#include + +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFUNIXStreamSocketTests: OTTestCase +@end + +@implementation OFUNIXStreamSocketTests +- (void)testUNIXStreamSocket +{ OFString *path; OFUNIXStreamSocket *sockClient, *sockServer, *sockAccepted; char buffer[5]; #if defined(OF_HAVE_FILES) && !defined(OF_IOS) @@ -43,52 +45,40 @@ */ path = [OFString stringWithFormat: @"/tmp/%@", [[OFUUID UUID] UUIDString]]; #endif - TEST(@"+[socket]", (sockClient = [OFUNIXStreamSocket socket]) && - (sockServer = [OFUNIXStreamSocket socket])) + sockClient = [OFUNIXStreamSocket socket]; + sockServer = [OFUNIXStreamSocket socket]; @try { - TEST(@"-[bindToPath:]", R([sockServer bindToPath: path])) + [sockServer bindToPath: path]; } @catch (OFBindSocketFailedException *e) { switch (e.errNo) { case EAFNOSUPPORT: case EPERM: - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut writeLine: - @"\r[OFUNIXStreamSocket] -[bindToPath:]: " - @"UNIX stream sockets unsupported, skipping tests"]; - - objc_autoreleasePoolPop(pool); - return; + OTSkip(@"UNIX stream sockets unsupported"); default: @throw e; } } @try { - TEST(@"-[listen]", R([sockServer listen])) - - TEST(@"-[connectToPath:]", - R([sockClient connectToPath: path])) - - TEST(@"-[accept]", (sockAccepted = [sockServer accept])) - - TEST(@"-[writeBuffer:length:]", - R([sockAccepted writeBuffer: "Hello" length: 5])) - - TEST(@"-[readIntoBuffer:length:]", - [sockClient readIntoBuffer: buffer length: 5] == 5 && - memcmp(buffer, "Hello", 5) == 0) - - TEST(@"-[remoteAddress]", OFSocketAddressUNIXPath( - sockAccepted.remoteAddress).length == 0) + [sockServer listen]; + + [sockClient connectToPath: path]; + + sockAccepted = [sockServer accept]; + [sockAccepted writeBuffer: "Hello" length: 5]; + + OTAssertEqual([sockClient readIntoBuffer: buffer length: 5], 5); + OTAssertEqual(memcmp(buffer, "Hello", 5), 0); + + OTAssertEqual(OFSocketAddressUNIXPath( + sockAccepted.remoteAddress).length, 0); } @finally { #ifdef OF_HAVE_FILES [[OFFileManager defaultManager] removeItemAtPath: path]; #endif } - - objc_autoreleasePoolPop(pool); } @end ADDED tests/OFUTF8StringTests.m Index: tests/OFUTF8StringTests.m ================================================================== --- /dev/null +++ tests/OFUTF8StringTests.m @@ -0,0 +1,30 @@ +/* + * 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 "OFStringTests.h" + +#import "OFUTF8String.h" + +@interface OFUTF8StringTests: OFStringTests +@end + +@implementation OFUTF8StringTests +- (Class)arrayClass +{ + return [OFUTF8String class]; +} +@end Index: tests/OFValueTests.m ================================================================== --- tests/OFValueTests.m +++ tests/OFValueTests.m @@ -15,148 +15,164 @@ #include "config.h" #include -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFValue"; - -@implementation TestsAppDelegate (OFValueTests) -- (void)valueTests -{ - void *pool = objc_autoreleasePoolPush(); - OFRange range = OFMakeRange(1, 64), range2; - OFPoint point = OFMakePoint(1.5f, 3.0f), point2; - OFSize size = OFMakeSize(4.5f, 5.0f), size2; - OFRect rect = OFMakeRect(1.5f, 3.0f, 4.5f, 6.0f), rect2; - OFValue *value; - void *pointer = &value; - - TEST(@"+[valueWithBytes:objCType:]", - (value = [OFValue valueWithBytes: &range - objCType: @encode(OFRange)])) - - TEST(@"-[objCType]", strcmp(value.objCType, @encode(OFRange)) == 0) - - TEST(@"-[getValue:size:]", - R([value getValue: &range2 size: sizeof(OFRange)]) && - OFEqualRanges(range2, range)) - - EXPECT_EXCEPTION(@"-[getValue:size:] with wrong size throws", - OFOutOfRangeException, - [value getValue: &range size: sizeof(OFRange) - 1]) - - TEST(@"+[valueWithPointer:]", - (value = [OFValue valueWithPointer: pointer])) - - TEST(@"-[pointerValue]", - value.pointerValue == pointer && - [[OFValue valueWithBytes: &pointer - objCType: @encode(void *)] pointerValue] == pointer) - - EXPECT_EXCEPTION(@"-[pointerValue] with wrong size throws", - OFOutOfRangeException, - [[OFValue valueWithBytes: "a" - objCType: @encode(char)] pointerValue]) - - TEST(@"+[valueWithNonretainedObject:]", - (value = [OFValue valueWithNonretainedObject: pointer])) - - TEST(@"-[nonretainedObjectValue]", - value.nonretainedObjectValue == pointer && - [[OFValue valueWithBytes: &pointer - objCType: @encode(id)] pointerValue] == pointer) - - EXPECT_EXCEPTION(@"-[nonretainedObjectValue] with wrong size throws", - OFOutOfRangeException, - [[OFValue valueWithBytes: "a" - objCType: @encode(char)] nonretainedObjectValue]) - - TEST(@"+[valueWithRange:]", - (value = [OFValue valueWithRange: range])) - - TEST(@"-[rangeValue]", - OFEqualRanges(value.rangeValue, range) && - (value = [OFValue valueWithBytes: &range - objCType: @encode(OFRange)]) && - OFEqualRanges(value.rangeValue, range)) - - TEST(@"-[getValue:size:] for OFRangeValue", - (value = [OFValue valueWithRange: range]) && - R([value getValue: &range2 size: sizeof(range2)]) && - OFEqualRanges(range2, range)) - - EXPECT_EXCEPTION(@"-[rangeValue] with wrong size throws", - OFOutOfRangeException, - [[OFValue valueWithBytes: "a" - objCType: @encode(char)] rangeValue]) - - TEST(@"+[valueWithPoint:]", - (value = [OFValue valueWithPoint: point])) - - TEST(@"-[pointValue]", - OFEqualPoints(value.pointValue, point) && - (value = [OFValue valueWithBytes: &point - objCType: @encode(OFPoint)]) && - OFEqualPoints(value.pointValue, point)) - - TEST(@"-[getValue:size:] for OFPointValue", - (value = [OFValue valueWithPoint: point]) && - R([value getValue: &point2 size: sizeof(point2)]) && - OFEqualPoints(point2, point)) - - EXPECT_EXCEPTION(@"-[pointValue] with wrong size throws", - OFOutOfRangeException, - [[OFValue valueWithBytes: "a" - objCType: @encode(char)] pointValue]) - - TEST(@"+[valueWithSize:]", - (value = [OFValue valueWithSize: size])) - - TEST(@"-[sizeValue]", - OFEqualSizes(value.sizeValue, size) && - (value = [OFValue valueWithBytes: &size - objCType: @encode(OFSize)]) && - OFEqualSizes(value.sizeValue, size)) - - TEST(@"-[getValue:size:] for OFSizeValue", - (value = [OFValue valueWithSize: size]) && - R([value getValue: &size2 size: sizeof(size2)]) && - OFEqualSizes(size2, size)) - - EXPECT_EXCEPTION(@"-[sizeValue] with wrong size throws", - OFOutOfRangeException, - [[OFValue valueWithBytes: "a" - objCType: @encode(char)] sizeValue]) - - TEST(@"+[valueWithRect:]", - (value = [OFValue valueWithRect: rect])) - - TEST(@"-[rectValue]", - OFEqualRects(value.rectValue, rect) && - (value = [OFValue valueWithBytes: &rect - objCType: @encode(OFRect)]) && - OFEqualRects(value.rectValue, rect)) - - TEST(@"-[getValue:size:] for OFRectValue", - (value = [OFValue valueWithRect: rect]) && - R([value getValue: &rect2 size: sizeof(rect2)]) && - OFEqualRects(rect2, rect)) - - EXPECT_EXCEPTION(@"-[rectValue] with wrong size throws", - OFOutOfRangeException, - [[OFValue valueWithBytes: "a" objCType: @encode(char)] rectValue]) - - TEST(@"-[isEqual:]", - [[OFValue valueWithRect: rect] - isEqual: [OFValue valueWithBytes: &rect - objCType: @encode(OFRect)]] && - ![[OFValue valueWithBytes: "a" objCType: @encode(signed char)] - isEqual: [OFValue valueWithBytes: "a" - objCType: @encode(unsigned char)]] && - ![[OFValue valueWithBytes: "a" objCType: @encode(char)] - isEqual: [OFValue valueWithBytes: "b" objCType: @encode(char)]]) - - objc_autoreleasePoolPop(pool); +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFValueTests: OTTestCase +@end + +@implementation OFValueTests +- (void)testObjCType +{ + OFRange range = OFMakeRange(1, 64); + OFValue *value = [OFValue valueWithBytes: &range + objCType: @encode(OFRange)]; + + OTAssertEqual(strcmp(value.objCType, @encode(OFRange)), 0); +} + +- (void)testGetValueSize +{ + OFRange range = OFMakeRange(1, 64), range2; + OFValue *value = [OFValue valueWithBytes: &range + objCType: @encode(OFRange)]; + + [value getValue: &range2 size: sizeof(OFRange)]; + OTAssert(OFEqualRanges(range2, range)); +} + +- (void)testGetValueSizeThrowsOnWrongSize +{ + OFRange range = OFMakeRange(1, 64); + OFValue *value = [OFValue valueWithBytes: &range + objCType: @encode(OFRange)]; + + OTAssertThrowsSpecific( + [value getValue: &range size: sizeof(OFRange) - 1], + OFOutOfRangeException); +} + +- (void)testPointer +{ + void *pointer = &pointer; + OFValue *value = [OFValue valueWithPointer: pointer]; + + OTAssertEqual(value.pointerValue, pointer); + OTAssertEqual([[OFValue valueWithBytes: &pointer + objCType: @encode(void *)] pointerValue], + pointer); + + OTAssertThrowsSpecific( + [[OFValue valueWithBytes: "a" + objCType: @encode(char)] pointerValue], + OFOutOfRangeException); +} + +- (void)testNonretainedObject +{ + id object = (id)&object; + OFValue *value = [OFValue valueWithNonretainedObject: object]; + + OTAssertEqual(value.nonretainedObjectValue, object); + OTAssertEqual([[OFValue + valueWithBytes: &object + objCType: @encode(id)] nonretainedObjectValue], object); + + OTAssertThrowsSpecific( + [[OFValue valueWithBytes: "a" + objCType: @encode(char)] nonretainedObjectValue], + OFOutOfRangeException); +} + +- (void)testRange +{ + OFRange range = OFMakeRange(1, 64), range2; + OFValue *value = [OFValue valueWithRange: range]; + + OTAssert(OFEqualRanges(value.rangeValue, range)); + OTAssert(OFEqualRanges( + [[OFValue valueWithBytes: &range + objCType: @encode(OFRange)] rangeValue], range)); + + [value getValue: &range2 size: sizeof(range2)]; + OTAssert(OFEqualRanges(range2, range)); + + OTAssertThrowsSpecific( + [[OFValue valueWithBytes: "a" + objCType: @encode(char)] rangeValue], + OFOutOfRangeException); +} + +- (void)testPoint +{ + OFPoint point = OFMakePoint(1.5f, 3.0f), point2; + OFValue *value = [OFValue valueWithPoint: point]; + + OTAssert(OFEqualPoints(value.pointValue, point)); + OTAssert(OFEqualPoints( + [[OFValue valueWithBytes: &point + objCType: @encode(OFPoint)] pointValue], point)); + + [value getValue: &point2 size: sizeof(point2)]; + OTAssert(OFEqualPoints(point2, point)); + + OTAssertThrowsSpecific( + [[OFValue valueWithBytes: "a" + objCType: @encode(char)] pointValue], + OFOutOfRangeException); +} + +- (void)testSize +{ + OFSize size = OFMakeSize(4.5f, 5.0f), size2; + OFValue *value = [OFValue valueWithSize: size]; + + OTAssert(OFEqualSizes(value.sizeValue, size)); + OTAssert(OFEqualSizes( + [[OFValue valueWithBytes: &size + objCType: @encode(OFSize)] sizeValue], size)); + + [value getValue: &size2 size: sizeof(size2)]; + OTAssert(OFEqualSizes(size2, size)); + + OTAssertThrowsSpecific( + [[OFValue valueWithBytes: "a" + objCType: @encode(char)] sizeValue], + OFOutOfRangeException); +} + +- (void)testRect +{ + OFRect rect = OFMakeRect(1.5f, 3.0f, 4.5f, 6.0f), rect2; + OFValue *value = [OFValue valueWithRect: rect]; + + OTAssert(OFEqualRects(value.rectValue, rect)); + OTAssert(OFEqualRects( + [[OFValue valueWithBytes: &rect + objCType: @encode(OFRect)] rectValue], rect)); + + [value getValue: &rect2 size: sizeof(rect2)]; + OTAssert(OFEqualRects(rect2, rect)); + + OTAssertThrowsSpecific( + [[OFValue valueWithBytes: "a" objCType: @encode(char)] rectValue], + OFOutOfRangeException); +} + +- (void)testIsEqual +{ + OFRect rect = OFMakeRect(1.5f, 3.0f, 4.5f, 6.0f); + + OTAssertEqualObjects([OFValue valueWithRect: rect], + [OFValue valueWithBytes: &rect + objCType: @encode(OFRect)]); + OTAssertNotEqualObjects( + [OFValue valueWithBytes: "a" objCType: @encode(signed char)], + [OFValue valueWithBytes: "a" objCType: @encode(unsigned char)]); + OTAssertNotEqualObjects( + [OFValue valueWithBytes: "a" objCType: @encode(char)], + [OFValue valueWithBytes: "b" objCType: @encode(char)]); } @end Index: tests/OFWindowsRegistryKeyTests.m ================================================================== --- tests/OFWindowsRegistryKeyTests.m +++ tests/OFWindowsRegistryKeyTests.m @@ -13,80 +13,115 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFWindowsRegistryKey"; - -@implementation TestsAppDelegate (OFWindowsRegistryKeyTests) -- (void)windowsRegistryKeyTests -{ - void *pool = objc_autoreleasePoolPush(); - OFData *data = [OFData dataWithItems: "abcdef" count: 6]; - OFWindowsRegistryKey *softwareKey, *objFWKey; +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFWindowsRegistryKeyTests: OTTestCase +{ + OFWindowsRegistryKey *_softwareKey, *_objFWKey; +} +@end + +@implementation OFWindowsRegistryKeyTests +- (void)setUp +{ + [super setUp]; + + _softwareKey = [[[OFWindowsRegistryKey currentUserKey] + openSubkeyAtPath: @"Software" + accessRights: KEY_ALL_ACCESS + options: 0] retain]; + _objFWKey = [[_softwareKey createSubkeyAtPath: @"ObjFW" + accessRights: KEY_ALL_ACCESS + securityAttributes: NULL + options: 0 + disposition: NULL] retain]; +} + +- (void)tearDown +{ + [_softwareKey deleteSubkeyAtPath: @"ObjFW"]; + + [super tearDown]; +} + +- (void)dealloc +{ + [_softwareKey release]; + [_objFWKey release]; + + [super dealloc]; +} + +- (void)testClassesRootKey +{ + OTAssertEqual([[OFWindowsRegistryKey classesRootKey] class], + [OFWindowsRegistryKey class]); +} + +- (void)testCurrentConfigKey +{ + OTAssertEqual([[OFWindowsRegistryKey currentConfigKey] class], + [OFWindowsRegistryKey class]); +} + +- (void)testCurrentUserKey +{ + OTAssertEqual([[OFWindowsRegistryKey currentUserKey] class], + [OFWindowsRegistryKey class]); +} + +- (void)testLocalMachineKey +{ + OTAssertEqual([[OFWindowsRegistryKey localMachineKey] class], + [OFWindowsRegistryKey class]); +} + +- (void)testOpenSubkeyAtPathAccessRightsOptionsThrowsForNonExistentKey +{ + OTAssertThrowsSpecific([[OFWindowsRegistryKey currentUserKey] + openSubkeyAtPath: @"nonexistent" + accessRights: KEY_ALL_ACCESS + options: 0], OFOpenWindowsRegistryKeyFailedException); +} + +- (void)testSetAndGetData +{ + OFData *data = [OFData dataWithItems: "abcdef" count: 6]; + DWORD type; + + [_objFWKey setData: data forValueNamed: @"data" type: REG_BINARY]; + OTAssertEqualObjects([_objFWKey dataForValueNamed: @"data" type: &type], + data); + OTAssertEqual(type, REG_BINARY); +} + +- (void)testSetAndGetString +{ DWORD type; - TEST(@"+[OFWindowsRegistryKey classesRootKey]", - [OFWindowsRegistryKey classesRootKey]) - - TEST(@"+[OFWindowsRegistryKey currentConfigKey]", - [OFWindowsRegistryKey currentConfigKey]) - - TEST(@"+[OFWindowsRegistryKey currentUserKey]", - [OFWindowsRegistryKey currentUserKey]) - - TEST(@"+[OFWindowsRegistryKey localMachineKey]", - [OFWindowsRegistryKey localMachineKey]) - - TEST(@"+[OFWindowsRegistryKey usersKey]", - [OFWindowsRegistryKey usersKey]) - - TEST(@"-[openSubkeyAtPath:accessRights:options:] #1", - (softwareKey = [[OFWindowsRegistryKey currentUserKey] - openSubkeyAtPath: @"Software" - accessRights: KEY_ALL_ACCESS - options: 0])) - - EXPECT_EXCEPTION(@"-[openSubkeyAtPath:accessRights:options:] #2", - OFOpenWindowsRegistryKeyFailedException, - [[OFWindowsRegistryKey currentUserKey] - openSubkeyAtPath: @"nonexistent" - accessRights: KEY_ALL_ACCESS - options: 0]) - - TEST(@"-[createSubkeyAtPath:accessRights:securityAttributes:options:" - @"disposition:]", - (objFWKey = [softwareKey createSubkeyAtPath: @"ObjFW" - accessRights: KEY_ALL_ACCESS - securityAttributes: NULL - options: 0 - disposition: NULL])) - - TEST(@"-[setData:forValueNamed:type:]", - R([objFWKey setData: data forValueNamed: @"data" type: REG_BINARY])) - - TEST(@"-[dataForValueNamed:subkeyPath:flags:type:]", - [[objFWKey dataForValueNamed: @"data" type: &type] isEqual: data] && - type == REG_BINARY) - - TEST(@"-[setString:forValueNamed:type:]", - R([objFWKey setString: @"foobar" forValueNamed: @"string"]) && - R([objFWKey setString: @"%PATH%;foo" - forValueNamed: @"expand" - type: REG_EXPAND_SZ])) - - TEST(@"-[stringForValue:subkeyPath:]", - [[objFWKey stringForValueNamed: @"string"] isEqual: @"foobar"] && - [[objFWKey stringForValueNamed: @"expand" type: &type] - isEqual: @"%PATH%;foo"] && - type == REG_EXPAND_SZ) - - TEST(@"-[deleteValueNamed:]", R([objFWKey deleteValueNamed: @"data"])) - - TEST(@"-[deleteSubkeyAtPath:]", - R([softwareKey deleteSubkeyAtPath: @"ObjFW"])) - - objc_autoreleasePoolPop(pool); + [_objFWKey setString: @"foobar" forValueNamed: @"string"]; + OTAssertEqualObjects([_objFWKey stringForValueNamed: @"string"], + @"foobar"); + + [_objFWKey setString: @"%PATH%;foo" + forValueNamed: @"expand" + type: REG_EXPAND_SZ]; + OTAssertEqualObjects([_objFWKey stringForValueNamed: @"expand" + type: &type], + @"%PATH%;foo"); + OTAssertEqual(type, REG_EXPAND_SZ); +} + +- (void)testDeleteValue +{ + [_objFWKey setString: @"foobar" forValueNamed: @"deleteme"]; + OTAssertEqualObjects([_objFWKey stringForValueNamed: @"deleteme"], + @"foobar"); + + [_objFWKey deleteValueNamed: @"deleteme"]; + OTAssertNil([_objFWKey stringForValueNamed: @"deleteme"]); } @end Index: tests/OFXMLElementBuilderTests.m ================================================================== --- tests/OFXMLElementBuilderTests.m +++ tests/OFXMLElementBuilderTests.m @@ -13,50 +13,58 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFXMLElementBuilderTests: OTTestCase +{ + OFXMLNode *_nodes[2]; + size_t _i; +} +@end -static OFString *const module = @"OFXMLElementBuilder"; -static OFXMLNode *nodes[2]; -static size_t i = 0; +@implementation OFXMLElementBuilderTests +- (void)dealloc +{ + [_nodes[0] release]; + [_nodes[1] release]; -@implementation TestsAppDelegate (OFXMLElementBuilderTests) + [super dealloc]; +} + - (void)elementBuilder: (OFXMLElementBuilder *)builder didBuildElement: (OFXMLElement *)element { - OFEnsure(i == 0); - nodes[i++] = [element retain]; + OTAssertEqual(_i, 0); + _nodes[_i++] = [element retain]; } - (void)elementBuilder: (OFXMLElementBuilder *)builder didBuildOrphanNode: (OFXMLNode *)node { - OFEnsure(i == 1); - nodes[i++] = [node retain]; + OTAssertEqual(_i, 1); + _nodes[_i++] = [node retain]; } -- (void)XMLElementBuilderTests +- (void)testElementBuilder { - void *pool = objc_autoreleasePoolPush(); OFXMLParser *parser = [OFXMLParser parser]; OFXMLElementBuilder *builder = [OFXMLElementBuilder builder]; OFString *string = @"barbaz" " " ""; parser.delegate = builder; builder.delegate = self; - TEST(@"Building elements from parsed XML", - R([parser parseString: string]) && - nodes[0] != nil && [nodes[0].XMLString isEqual: string] && - R([parser parseString: @""]) && - nodes[1] != nil && [nodes[1].XMLString isEqual: @""] && - i == 2) - - [nodes[0] release]; - [nodes[1] release]; - objc_autoreleasePoolPop(pool); + [parser parseString: string]; + OTAssertEqualObjects(_nodes[0].XMLString, string); + + [parser parseString: @""]; + OTAssertEqualObjects(_nodes[1].XMLString, @""); + + OTAssertEqual(_i, 2); } @end Index: tests/OFXMLNodeTests.m ================================================================== --- tests/OFXMLNodeTests.m +++ tests/OFXMLNodeTests.m @@ -13,125 +13,194 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" - -static OFString *module; - -@implementation TestsAppDelegate (OFXMLNodeTests) -- (void)XMLNodeTests -{ - void *pool = objc_autoreleasePoolPush(); - id node1, node2, node3, node4; - OFArray *array; - - module = @"OFXMLNode"; - - TEST(@"+[elementWithName:]", - (node1 = [OFXMLElement elementWithName: @"foo"]) && - [[node1 XMLString] isEqual: @""]) - - TEST(@"+[elementWithName:stringValue:]", - (node2 = [OFXMLElement elementWithName: @"foo" - stringValue: @"b&ar"]) && - [[node2 XMLString] isEqual: @"b&ar"]) - - TEST(@"+[elementWithName:namespace:]", - (node3 = [OFXMLElement elementWithName: @"foo" - namespace: @"urn:objfw:test"]) && - R([node3 addAttributeWithName: @"test" stringValue: @"test"]) && - R([node3 setPrefix: @"objfw-test" - forNamespace: @"urn:objfw:test"]) && - [[node3 XMLString] isEqual: @""] && - (node4 = [OFXMLElement elementWithName: @"foo" - namespace: @"urn:objfw:test"]) && - R([node4 addAttributeWithName: @"test" stringValue: @"test"]) && - [[node4 XMLString] isEqual: - @""]) - - TEST(@"+[elementWithName:namespace:stringValue:]", - (node4 = [OFXMLElement elementWithName: @"foo" - namespace: @"urn:objfw:test" - stringValue: @"x"]) && - R([node4 setPrefix: @"objfw-test" - forNamespace: @"urn:objfw:test"]) && - [[node4 XMLString] isEqual: @"x"]) - - TEST(@"+[charactersWithString:]", - (node4 = [OFXMLCharacters charactersWithString: @""]) && - [[node4 XMLString] isEqual: @"<foo>"]) - - TEST(@"+[CDATAWithString:]", - (node4 = [OFXMLCDATA CDATAWithString: @""]) && - [[node4 XMLString] isEqual: @"]]>"]); - - TEST(@"+[commentWithText:]", - (node4 = [OFXMLComment commentWithText: @" comment "]) && - [[node4 XMLString] isEqual: @""]) - - module = @"OFXMLElement"; - - TEST(@"-[addAttributeWithName:stringValue:]", - R([node1 addAttributeWithName: @"foo" stringValue: @"b&ar"]) && - [[node1 XMLString] isEqual: @""] && - R([node2 addAttributeWithName: @"foo" stringValue: @"b&ar"]) && - [[node2 XMLString] isEqual: @"b&ar"]) - - TEST(@"-[setPrefix:forNamespace:]", - R([node2 setPrefix: @"objfw-test" - forNamespace: @"urn:objfw:test"])) - - TEST(@"-[addAttributeWithName:namespace:stringValue:]", - R([node2 addAttributeWithName: @"foo" - namespace: @"urn:objfw:test" - stringValue: @"bar"]) && - R([node2 addAttributeWithName: @"foo" - namespace: @"urn:objfw:test" - stringValue: @"ignored"]) && - [[node2 XMLString] isEqual: - @"b&ar"]) - - TEST(@"-[removeAttributeForName:namespace:]", - R([node2 removeAttributeForName: @"foo"]) && - [[node2 XMLString] isEqual: - @"b&ar"] && - R([node2 removeAttributeForName: @"foo" - namespace: @"urn:objfw:test"]) && - [[node2 XMLString] isEqual: @"b&ar"]) - - TEST(@"-[addChild:]", - R([node1 addChild: [OFXMLElement elementWithName: @"bar"]]) && - [[node1 XMLString] isEqual: - @""] && - R([node3 addChild: [OFXMLElement elementWithName: @"bar" - namespace: @"urn:objfw:test"]]) && - [[node3 XMLString] isEqual: - @""]) - - TEST(@"+[elementWithXMLString:] and -[stringValue]", - [[[OFXMLElement elementWithXMLString: +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFXMLNodeTests: OTTestCase +@end + +@implementation OFXMLNodeTests +- (void)testElementWithName +{ + OTAssertEqualObjects( + [[OFXMLElement elementWithName: @"foo"] XMLString], + @""); +} + +- (void)testElementWithNameStringValue +{ + OTAssertEqualObjects( + [[OFXMLElement elementWithName: @"foo" + stringValue: @"b&ar"] XMLString], + @"b&ar"); +} + +- (void)testElementWithNameNamespace +{ + OFXMLElement *element; + + element = [OFXMLElement elementWithName: @"foo" + namespace: @"urn:objfw:test"]; + [element addAttributeWithName: @"test" stringValue: @"test"]; + [element setPrefix: @"objfw-test" forNamespace: @"urn:objfw:test"]; + OTAssertEqualObjects(element.XMLString, + @""); + + element = [OFXMLElement elementWithName: @"foo" + namespace: @"urn:objfw:test"]; + [element addAttributeWithName: @"test" stringValue: @"test"]; + OTAssertEqualObjects(element.XMLString, + @""); +} + +- (void)testElementWithNameNamespaceStringValue +{ + OFXMLElement *element = [OFXMLElement elementWithName: @"foo" + namespace: @"urn:objfw:test" + stringValue: @"x"]; + [element setPrefix: @"objfw-test" forNamespace: @"urn:objfw:test"]; + OTAssertEqualObjects(element.XMLString, + @"x"); +} + +- (void)testElementWithXMLStringAndStringValue +{ + OTAssertEqualObjects([[OFXMLElement elementWithXMLString: @"\r\nfoo" - @"bazqux"] stringValue] - isEqual: @"foobarbazqux"]) - - TEST(@"-[elementsForName:namespace:]", - (array = [node3 elementsForName: @"bar" - namespace: @"urn:objfw:test"]) && - array.count == 1 && [[array.firstObject XMLString] isEqual: - @""]) - - TEST(@"-[isEqual:]", - [[OFXMLElement elementWithXMLString: @""] isEqual: - [OFXMLElement elementWithXMLString: @""]] && - [[OFXMLElement elementWithXMLString: @""] isEqual: - [OFXMLElement elementWithXMLString: @""]]) - - TEST(@"-[XMLStringWithIndentation:]", - [[[OFXMLElement elementWithXMLString: @"a\nb" - @""] XMLStringWithIndentation: 2] isEqual: - @"\n \n a\nb\n \n \n"]) - - objc_autoreleasePoolPop(pool); + @"bazqux"] stringValue], + @"foobarbazqux"); +} + +- (void)testCharactersWithString +{ + OTAssertEqualObjects( + [[OFXMLCharacters charactersWithString: @""] XMLString], + @"<foo>"); +} + +- (void)testCDATAWithString +{ + OTAssertEqualObjects( + [[OFXMLCDATA CDATAWithString: @""] XMLString], + @"]]>"); +} + +- (void)testCommentWithText +{ + OTAssertEqualObjects( + [[OFXMLComment commentWithText: @" comment "] XMLString], + @""); +} + +- (void)testIsEqual +{ + OTAssertEqualObjects( + [OFXMLElement elementWithXMLString: @""], + [OFXMLElement elementWithXMLString: @""]); + + OTAssertEqualObjects( + [OFXMLElement elementWithXMLString: @""], + [OFXMLElement elementWithXMLString: @""]); + + OTAssertNotEqualObjects( + [OFXMLElement elementWithXMLString: @""], + [OFXMLElement elementWithXMLString: @""]); +} + +- (void)testHash +{ + OTAssertEqual( + [[OFXMLElement elementWithXMLString: @""] hash], + [[OFXMLElement elementWithXMLString: @""] + hash]); + + OTAssertEqual( + [[OFXMLElement elementWithXMLString: @""] hash], + [[OFXMLElement elementWithXMLString: @""] hash]); + + OTAssertNotEqual( + [[OFXMLElement elementWithXMLString: @""] hash], + [[OFXMLElement elementWithXMLString: @""] hash]); +} + +- (void)testAddAttributeWithNameStringValue +{ + OFXMLElement *element = [OFXMLElement elementWithName: @"foo" + stringValue: @"b&ar"]; + + [element setPrefix: @"objfw-test" forNamespace: @"urn:objfw:test"]; + [element addAttributeWithName: @"foo" + stringValue: @"b&ar"]; + [element addAttributeWithName: @"foo" + namespace: @"urn:objfw:test" + stringValue: @"bar"]; + + OTAssertEqualObjects(element.XMLString, + @"b&ar"); +} + +- (void)testRemoveAttributeForNameNamespace +{ + OFXMLElement *element = [OFXMLElement elementWithName: @"foo" + stringValue: @"b&ar"]; + + [element setPrefix: @"objfw-test" forNamespace: @"urn:objfw:test"]; + [element addAttributeWithName: @"foo" + stringValue: @"b&ar"]; + [element addAttributeWithName: @"foo" + namespace: @"urn:objfw:test" + stringValue: @"bar"]; + + [element removeAttributeForName: @"foo"]; + OTAssertEqualObjects(element.XMLString, + @"b&ar"); + + [element removeAttributeForName: @"foo" namespace: @"urn:objfw:test"]; + OTAssertEqualObjects(element.XMLString, @"b&ar"); +} + +- (void)testAddChild +{ + OFXMLElement *element; + + element = [OFXMLElement elementWithName: @"foo"]; + [element addAttributeWithName: @"foo" stringValue: @"b&ar"]; + [element addChild: [OFXMLElement elementWithName: @"bar"]]; + OTAssertEqualObjects(element.XMLString, + @""); + + element = [OFXMLElement elementWithName: @"foo" + namespace: @"urn:objfw:test"]; + [element setPrefix: @"objfw-test" forNamespace: @"urn:objfw:test"]; + [element addAttributeWithName: @"test" stringValue: @"test"]; + [element addChild: [OFXMLElement elementWithName: @"bar" + namespace: @"urn:objfw:test"]]; + OTAssertEqualObjects(element.XMLString, + @""); +} + +- (void)testElementsForNameNamespace +{ + OFXMLElement *element = [OFXMLElement elementWithName: @"foo"]; + OFXMLElement *bar; + + [element addChild: [OFXMLElement elementWithName: @"foo"]]; + bar = [OFXMLElement elementWithName: @"bar" + namespace: @"urn:objfw:test"]; + [element addChild: bar]; + + OTAssertEqualObjects([element elementsForName: @"bar" + namespace: @"urn:objfw:test"], + [OFArray arrayWithObject: bar]); +} + +- (void)testXMLStringWithIndentation +{ + OTAssertEqualObjects([[OFXMLElement + elementWithXMLString: @"a\nb"] + XMLStringWithIndentation: 2], + @"\n \n a\nb\n \n \n"); } @end Index: tests/OFXMLParserTests.m ================================================================== --- tests/OFXMLParserTests.m +++ tests/OFXMLParserTests.m @@ -16,14 +16,18 @@ #include "config.h" #include #include -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFXMLParser"; -static int i = 0; +@interface OFXMLParserTests: OTTestCase +{ + int _i; +} +@end enum EventType { eventTypeProcessingInstruction, eventTypeTagOpen, eventTypeTagClose, @@ -30,247 +34,224 @@ eventTypeString, eventTypeCDATA, eventTypeComment }; -@implementation TestsAppDelegate (OFXMLParser) +@implementation OFXMLParserTests - (void)parser: (OFXMLParser *)parser didCreateEvent: (enum EventType)type name: (OFString *)name prefix: (OFString *)prefix namespace: (OFString *)namespace attributes: (OFArray *)attrs string: (OFString *)string { - OFString *message; - - i++; - message = [OFString stringWithFormat: @"Parsing part #%d", i]; - - switch (i) { + switch (_i++) { + case 0: + OTAssertEqual(type, eventTypeProcessingInstruction); + OTAssertEqualObjects(name, @"xml"); + OTAssertEqualObjects(string, @"version='1.0'"); + break; case 1: - TEST(message, - type == eventTypeProcessingInstruction && - [name isEqual: @"xml"] && - [string isEqual: @"version='1.0'"]) + OTAssertEqual(type, eventTypeProcessingInstruction); + OTAssertEqualObjects(name, @"p?i"); + OTAssertNil(string); break; case 2: - TEST(message, - type == eventTypeProcessingInstruction && - [name isEqual: @"p?i"] && string == nil) + OTAssertEqual(type, eventTypeTagOpen); + OTAssertEqualObjects(name, @"root"); + OTAssertNil(prefix); + OTAssertNil(namespace); + OTAssertEqual(attrs.count, 0); break; case 3: - TEST(message, - type == eventTypeTagOpen && [name isEqual: @"root"] && - prefix == nil && namespace == nil && attrs.count == 0) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n\n "); break; case 4: - TEST(message, - type == eventTypeString && [string isEqual: @"\n\n "]) + OTAssertEqual(type, eventTypeCDATA); + OTAssertEqualObjects(string, @"f<]]]oo]"); + OTAssertEqual(parser.lineNumber, 3); break; case 5: - TEST(message, - type == eventTypeCDATA && [string isEqual: @"f<]]]oo]"] && - parser.lineNumber == 3) + OTAssertEqual(type, eventTypeTagOpen); + OTAssertEqualObjects(name, @"bar"); + OTAssertNil(prefix); + OTAssertNil(namespace); + OTAssertNil(attrs); break; case 6: - TEST(message, - type == eventTypeTagOpen && [name isEqual: @"bar"] && - prefix == nil && namespace == nil && attrs == nil) + OTAssertEqual(type, eventTypeTagClose); + OTAssertEqualObjects(name, @"bar"); + OTAssertNil(prefix); + OTAssertNil(namespace); + OTAssertNil(attrs); break; case 7: - TEST(message, - type == eventTypeTagClose && [name isEqual: @"bar"] && - prefix == nil && namespace == nil && attrs == nil) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n "); break; case 8: - TEST(message, - type == eventTypeString && [string isEqual: @"\n "]) + OTAssertEqual(type, eventTypeTagOpen); + OTAssertEqualObjects(name, @"foobar"); + OTAssertNil(prefix); + OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar"); + OTAssertEqualObjects(attrs, [OFArray arrayWithObject: + [OFXMLAttribute attributeWithName: @"xmlns" + stringValue: @"urn:objfw:test:" + @"foobar"]]); break; case 9: - TEST(message, - type == eventTypeTagOpen && [name isEqual: @"foobar"] && - prefix == nil && - [namespace isEqual: @"urn:objfw:test:foobar"] && - attrs.count == 1 && - /* xmlns attr */ - [[[attrs objectAtIndex: 0] name] isEqual: @"xmlns"] && - [[attrs objectAtIndex: 0] namespace] == nil && - [[[attrs objectAtIndex: 0] stringValue] isEqual: - @"urn:objfw:test:foobar"]) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n "); break; case 10: - TEST(message, - type == eventTypeString && [string isEqual: @"\n "]) + OTAssertEqual(type, eventTypeTagOpen); + OTAssertEqualObjects(name, @"qux"); + OTAssertNil(prefix); + OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar"); + OTAssertEqualObjects(attrs, [OFArray arrayWithObject: + [OFXMLAttribute attributeWithName: @"foo" + namespace: @"http://www.w3.org/" + @"2000/xmlns/" + stringValue: @"urn:objfw:test:foo"]]); break; case 11: - TEST(message, - type == eventTypeTagOpen && [name isEqual: @"qux"] && - prefix == nil && - [namespace isEqual: @"urn:objfw:test:foobar"] && - attrs.count == 1 && - /* xmlns:foo attr */ - [[[attrs objectAtIndex: 0] name] isEqual: @"foo"] && - [[[attrs objectAtIndex: 0] namespace] isEqual: - @"http://www.w3.org/2000/xmlns/"] && - [[[attrs objectAtIndex: 0] stringValue] isEqual: - @"urn:objfw:test:foo"]) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n "); break; case 12: - TEST(message, - type == eventTypeString && [string isEqual: @"\n "]) + OTAssertEqual(type, eventTypeTagOpen); + OTAssertEqualObjects(name, @"bla"); + OTAssertEqualObjects(prefix, @"foo"); + OTAssertEqualObjects(namespace, @"urn:objfw:test:foo"); + OTAssertEqualObjects(attrs, ([OFArray arrayWithObjects: + [OFXMLAttribute attributeWithName: @"bla" + namespace: @"urn:objfw:test:foo" + stringValue: @"bla"], + [OFXMLAttribute attributeWithName: @"blafoo" + stringValue: @"foo"], nil])); break; case 13: - TEST(message, - type == eventTypeTagOpen && [name isEqual: @"bla"] && - [prefix isEqual: @"foo"] && - [namespace isEqual: @"urn:objfw:test:foo"] && - attrs.count == 2 && - /* foo:bla attr */ - [[[attrs objectAtIndex: 0] name] isEqual: @"bla"] && - [[[attrs objectAtIndex: 0] namespace] isEqual: - @"urn:objfw:test:foo"] && - [[[attrs objectAtIndex: 0] stringValue] isEqual: @"bla"] && - /* blafoo attr */ - [[[attrs objectAtIndex: 1] name] isEqual: @"blafoo"] && - [[attrs objectAtIndex: 1] namespace] == nil && - [[[attrs objectAtIndex: 1] stringValue] isEqual: @"foo"]) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n "); break; case 14: - TEST(message, - type == eventTypeString && [string isEqual: @"\n "]) + OTAssertEqual(type, eventTypeTagOpen); + OTAssertEqualObjects(name, @"blup"); + OTAssertNil(prefix); + OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar"); + OTAssertEqualObjects(attrs, ([OFArray arrayWithObjects: + [OFXMLAttribute attributeWithName: @"qux" + namespace: @"urn:objfw:test:foo" + stringValue: @"asd"], + [OFXMLAttribute attributeWithName: @"quxqux" + stringValue: @"test"], nil])); break; case 15: - TEST(message, - type == eventTypeTagOpen && [name isEqual: @"blup"] && - prefix == nil && - [namespace isEqual: @"urn:objfw:test:foobar"] && - attrs.count == 2 && - /* foo:qux attr */ - [[[attrs objectAtIndex: 0] name] isEqual: @"qux"] && - [[[attrs objectAtIndex: 0] namespace] isEqual: - @"urn:objfw:test:foo"] && - [[[attrs objectAtIndex: 0] stringValue] isEqual: @"asd"] && - /* quxqux attr */ - [[[attrs objectAtIndex: 1] name] isEqual: @"quxqux"] && - [[attrs objectAtIndex: 1] namespace] == nil && - [[[attrs objectAtIndex: 1] stringValue] isEqual: @"test"]) + OTAssertEqual(type, eventTypeTagClose); + OTAssertEqualObjects(name, @"blup"); + OTAssertNil(prefix); + OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar"); break; case 16: - TEST(message, - type == eventTypeTagClose && [name isEqual: @"blup"] && - prefix == nil && - [namespace isEqual: @"urn:objfw:test:foobar"]) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n "); break; case 17: - TEST(message, - type == eventTypeString && [string isEqual: @"\n "]) + OTAssertEqual(type, eventTypeTagOpen); + OTAssertEqualObjects(name, @"bla"); + OTAssertEqualObjects(prefix, @"bla"); + OTAssertEqualObjects(namespace, @"urn:objfw:test:bla"); + OTAssertEqualObjects(attrs, ([OFArray arrayWithObjects: + [OFXMLAttribute attributeWithName: @"bla" + namespace: @"http://www.w3.org/" + @"2000/xmlns/" + stringValue: @"urn:objfw:test:bla"], + [OFXMLAttribute attributeWithName: @"qux" + stringValue: @"qux"], + [OFXMLAttribute attributeWithName: @"foo" + namespace: @"urn:objfw:test:bla" + stringValue: @"blafoo"], nil])); break; case 18: - TEST(message, - type == eventTypeTagOpen && [name isEqual: @"bla"] && - [prefix isEqual: @"bla"] && - [namespace isEqual: @"urn:objfw:test:bla"] && - attrs.count == 3 && - /* xmlns:bla attr */ - [[[attrs objectAtIndex: 0] name] isEqual: @"bla"] && - [[[attrs objectAtIndex: 0] namespace] isEqual: - @"http://www.w3.org/2000/xmlns/"] && - [[[attrs objectAtIndex: 0] stringValue] isEqual: - @"urn:objfw:test:bla"] && - /* qux attr */ - [[[attrs objectAtIndex: 1] name] isEqual: @"qux"] && - [[attrs objectAtIndex: 1] namespace] == nil && - [[[attrs objectAtIndex: 1] stringValue] isEqual: @"qux"] && - /* bla:foo attr */ - [[[attrs objectAtIndex: 2] name] isEqual: @"foo"] && - [[[attrs objectAtIndex: 2] namespace] isEqual: - @"urn:objfw:test:bla"] && - [[[attrs objectAtIndex: 2] stringValue] isEqual: @"blafoo"]) + OTAssertEqual(type, eventTypeTagClose); + OTAssertEqualObjects(name, @"bla"); + OTAssertEqualObjects(prefix, @"bla"); + OTAssertEqualObjects(namespace, @"urn:objfw:test:bla"); break; case 19: - TEST(message, - type == eventTypeTagClose && [name isEqual: @"bla"] && - [prefix isEqual: @"bla"] && - [namespace isEqual: @"urn:objfw:test:bla"]) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n "); break; case 20: - TEST(message, - type == eventTypeString && [string isEqual: @"\n "]) + OTAssertEqual(type, eventTypeTagOpen); + OTAssertEqualObjects(name, @"abc"); + OTAssertNil(prefix); + OTAssertEqualObjects(namespace, @"urn:objfw:test:abc"); + OTAssertEqualObjects(attrs, ([OFArray arrayWithObjects: + [OFXMLAttribute attributeWithName: @"xmlns" + stringValue: @"urn:objfw:test:abc"], + [OFXMLAttribute attributeWithName: @"abc" + stringValue: @"abc"], + [OFXMLAttribute attributeWithName: @"abc" + namespace: @"urn:objfw:test:foo" + stringValue: @"abc"], nil])); break; case 21: - TEST(message, - type == eventTypeTagOpen && [name isEqual: @"abc"] && - prefix == nil && - [namespace isEqual: @"urn:objfw:test:abc"] && - attrs.count == 3 && - /* xmlns attr */ - [[[attrs objectAtIndex: 0] name] isEqual: @"xmlns"] && - [[attrs objectAtIndex: 0] namespace] == nil && - [[[attrs objectAtIndex: 0] stringValue] isEqual: - @"urn:objfw:test:abc"] && - /* abc attr */ - [[[attrs objectAtIndex: 1] name] isEqual: @"abc"] && - [[attrs objectAtIndex: 1] namespace] == nil && - [[[attrs objectAtIndex: 1] stringValue] isEqual: @"abc"] && - /* foo:abc attr */ - [[[attrs objectAtIndex: 2] name] isEqual: @"abc"] && - [[[attrs objectAtIndex: 2] namespace] isEqual: - @"urn:objfw:test:foo"] && - [[[attrs objectAtIndex: 2] stringValue] isEqual: @"abc"]) + OTAssertEqual(type, eventTypeTagClose); + OTAssertEqualObjects(name, @"abc"); + OTAssertNil(prefix); + OTAssertEqualObjects(namespace, @"urn:objfw:test:abc"); break; case 22: - TEST(message, - type == eventTypeTagClose && [name isEqual: @"abc"] && - prefix == nil && [namespace isEqual: @"urn:objfw:test:abc"]) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n "); break; case 23: - TEST(message, - type == eventTypeString && [string isEqual: @"\n "]) + OTAssertEqual(type, eventTypeTagClose); + OTAssertEqualObjects(name, @"bla"); + OTAssertEqualObjects(prefix, @"foo"); + OTAssertEqualObjects(namespace, @"urn:objfw:test:foo"); break; case 24: - TEST(message, - type == eventTypeTagClose && [name isEqual: @"bla"] && - [prefix isEqual: @"foo"] && - [namespace isEqual: @"urn:objfw:test:foo"]) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n "); break; case 25: - TEST(message, - type == eventTypeString && [string isEqual: @"\n "]) + OTAssertEqual(type, eventTypeComment); + OTAssertEqualObjects(string, @" commänt "); break; case 26: - TEST(message, - type == eventTypeComment && [string isEqual: @" commänt "]) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n "); break; case 27: - TEST(message, - type == eventTypeString && [string isEqual: @"\n "]) + OTAssertEqual(type, eventTypeTagClose); + OTAssertEqualObjects(name, @"qux"); + OTAssertNil(prefix); + OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar"); break; case 28: - TEST(message, - type == eventTypeTagClose && [name isEqual: @"qux"] && - prefix == nil && - [namespace isEqual: @"urn:objfw:test:foobar"]) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n "); break; case 29: - TEST(message, - type == eventTypeString && [string isEqual: @"\n "]) + OTAssertEqual(type, eventTypeTagClose); + OTAssertEqualObjects(name, @"foobar"); + OTAssertNil(prefix); + OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar"); break; case 30: - TEST(message, - type == eventTypeTagClose && [name isEqual: @"foobar"] && - prefix == nil && - [namespace isEqual: @"urn:objfw:test:foobar"]) + OTAssertEqual(type, eventTypeString); + OTAssertEqualObjects(string, @"\n"); break; case 31: - TEST(message, - type == eventTypeString && [string isEqual: @"\n"]) - break; - case 32: - TEST(message, - type == eventTypeTagClose && [name isEqual: @"root"] && - prefix == nil && namespace == nil); + OTAssertEqual(type, eventTypeTagClose); + OTAssertEqualObjects(name, @"root"); + OTAssertNil(prefix); + OTAssertNil(namespace); break; } } - (void)parser: (OFXMLParser *)parser @@ -355,14 +336,13 @@ return @"foobar"; return nil; } -- (void)XMLParserTests +- (void)testParser { - void *pool = objc_autoreleasePoolPush(); - const char *string = "\xEF\xBB\xBF" + static const char *string = "\xEF\xBB\xBF" "\r\r" " \n" " \r\n" " \n" " \n" @@ -376,13 +356,12 @@ " \n" ""; OFXMLParser *parser; size_t j, length; - TEST(@"+[parser]", (parser = [OFXMLParser parser])) - - TEST(@"-[setDelegate:]", (parser.delegate = self)) + parser = [OFXMLParser parser]; + parser.delegate = self; /* Simulate a stream where we only get chunks */ length = strlen(string); for (j = 0; j < length; j+= 2) { @@ -393,40 +372,44 @@ [parser parseBuffer: string + j length: 1]; else [parser parseBuffer: string + j length: 2]; } - TEST(@"Checking if everything was parsed", - i == 32 && parser.lineNumber == 18) - - TEST(@"-[hasFinishedParsing]", parser.hasFinishedParsing) - - TEST(@"Parsing whitespaces after the document", - R([parser parseString: @" \t\r\n "])) - - TEST(@"Parsing comments after the document", - R([parser parseString: @" \t\r\n "])) - - EXPECT_EXCEPTION(@"Detection of junk after the document #1", - OFMalformedXMLException, [parser parseString: @"a"]) - - EXPECT_EXCEPTION(@"Detection of junk after the document #2", - OFMalformedXMLException, [parser parseString: @""]) - - parser = [OFXMLParser parser]; - EXPECT_EXCEPTION(@"Detection of invalid XML processing instruction #2", - OFInvalidEncodingException, - [parser parseString: @""]) - - parser = [OFXMLParser parser]; - EXPECT_EXCEPTION(@"Detection of invalid XML processing instruction #3", - OFMalformedXMLException, - [parser parseString: @""]) - - objc_autoreleasePoolPop(pool); + OTAssertEqual(_i, 32); + OTAssertEqual(parser.lineNumber, 18); + OTAssertTrue(parser.hasFinishedParsing); + + /* Parsing whitespaces after the document */ + [parser parseString: @" \t\r\n "]; + + /* Parsing comments after the document */ + [parser parseString: @" \t\r\n "]; + + /* Detection of junk after the document */ + OTAssertThrowsSpecific([parser parseString: @"a"], + OFMalformedXMLException); + OTAssertThrowsSpecific([parser parseString: @""], + OFMalformedXMLException); + + parser = [OFXMLParser parser]; + OTAssertThrowsSpecific([parser parseString: @""], + OFMalformedXMLException); +} + +- (void)testDetectionOfInvalidEncoding +{ + OFXMLParser *parser = [OFXMLParser parser]; + + OTAssertThrowsSpecific( + [parser parseString: @""], + OFInvalidEncodingException); } @end Index: tests/RuntimeARCTests.m ================================================================== --- tests/RuntimeARCTests.m +++ tests/RuntimeARCTests.m @@ -13,18 +13,38 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface RuntimeARCTests: OTTestCase +@end + +@interface RuntimeARCTestClass: OFObject +@end + +@implementation RuntimeARCTests +- (void)testExceptionsDuringInit +{ + OTAssertThrows((void)[[RuntimeARCTestClass alloc] init]); +} + +- (void)testWeakReferences +{ + id object = [[OFObject alloc] init]; + __weak id weak = object; -static OFString *const module = @"Runtime (ARC)"; + OTAssertEqual(weak, object); -@interface RuntimeARCTest: OFObject + object = nil; + OTAssertNil(weak); +} @end -@implementation RuntimeARCTest +@implementation RuntimeARCTestClass - (instancetype)init { self = [super init]; #if defined(OF_WINDOWS) && defined(OF_AMD64) @@ -38,24 +58,6 @@ @throw [OFException exception]; #endif return self; } -@end - -@implementation TestsAppDelegate (RuntimeARCTests) -- (void)runtimeARCTests -{ - id object; - __weak id weak; - - EXPECT_EXCEPTION(@"Exceptions in init", OFException, - object = [[RuntimeARCTest alloc] init]) - - object = [[OFObject alloc] init]; - weak = object; - TEST(@"weakly referencing an object", weak == object) - - object = nil; - TEST(@"weak references becoming nil", weak == nil) -} @end Index: tests/RuntimeTests.m ================================================================== --- tests/RuntimeTests.m +++ tests/RuntimeTests.m @@ -13,20 +13,16 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"Runtime"; static void *testKey = &testKey; -@interface OFObject (SuperTest) -- (id)superTest; -@end - -@interface RuntimeTest: OFObject +@interface RuntimeTestClass: OFObject { OFString *_foo, *_bar; } @property (nonatomic, copy) OFString *foo; @@ -33,11 +29,121 @@ @property (retain) OFString *bar; - (id)nilSuperTest; @end -@implementation RuntimeTest +@interface RuntimeTests: OTTestCase +{ + RuntimeTestClass *_test; +} +@end + +@interface OFObject (SuperTest) +- (id)superTest; +@end + +@implementation RuntimeTests +- (void)setUp +{ + [super setUp]; + + _test = [[RuntimeTestClass alloc] init]; +} + +- (void)dealloc +{ + [_test release]; + + [super dealloc]; +} + +- (void)testCallNonExistentMethodViaSuper +{ + OTAssertThrowsSpecific([_test superTest], OFNotImplementedException); +} + +- (void)testCallMethodViaSuperWithNilSelf +{ + OTAssertNil([_test nilSuperTest]); +} + +- (void)testPropertyCopyNonatomic +{ + OFMutableString *string = [OFMutableString stringWithString: @"foo"]; + OFString *foo = @"foo"; + + _test.foo = string; + OTAssertEqualObjects(_test.foo, foo); + OTAssertNotEqual(_test.foo, foo); + OTAssertEqual(_test.foo.retainCount, 1); +} + +- (void)testPropertyRetainAtomic +{ + OFMutableString *string = [OFMutableString stringWithString: @"foo"]; + + _test.bar = string; + OTAssertEqual(_test.bar, string); + OTAssertEqual(string.retainCount, 3); +} + +- (void)testAssociatedObjects +{ + objc_setAssociatedObject(self, testKey, _test, OBJC_ASSOCIATION_ASSIGN); + OTAssertEqual(_test.retainCount, 1); + + objc_setAssociatedObject(self, testKey, _test, OBJC_ASSOCIATION_RETAIN); + OTAssertEqual(_test.retainCount, 2); + + OTAssertEqual(objc_getAssociatedObject(self, testKey), _test); + OTAssertEqual(_test.retainCount, 3); + + objc_setAssociatedObject(self, testKey, _test, OBJC_ASSOCIATION_ASSIGN); + OTAssertEqual(_test.retainCount, 2); + + objc_setAssociatedObject(self, testKey, _test, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + OTAssertEqual(_test.retainCount, 3); + + OTAssertEqual(objc_getAssociatedObject(self, testKey), _test); + OTAssertEqual(_test.retainCount, 3); + + objc_removeAssociatedObjects(self); + OTAssertEqual(_test.retainCount, 2); +} + +#ifdef OF_OBJFW_RUNTIME +- (void)testTaggedPointers +{ + int classID; + uintmax_t value; + id object; + + if (sizeof(uintptr_t) == 8) + value = 0xDEADBEEFDEADBEF; + else if (sizeof(uintptr_t) == 4) + value = 0xDEADBEF; + else + OTAssert(sizeof(uintptr_t) == 8 || sizeof(uintptr_t) == 4); + + OTAssertNotEqual(objc_registerTaggedPointerClass([OFString class]), -1); + + classID = objc_registerTaggedPointerClass([OFNumber class]); + OTAssertNotEqual(classID, -1); + + object = objc_createTaggedPointer(classID, (uintptr_t)value); + OTAssertNotNil(object); + OTAssertEqual(object_getClass(object), [OFNumber class]); + OTAssertEqual([object class], [OFNumber class]); + OTAssertEqual(object_getTaggedPointerValue(object), value); + OTAssertNotNil(objc_createTaggedPointer(classID, UINTPTR_MAX >> 4)); + OTAssertNil(objc_createTaggedPointer(classID, (UINTPTR_MAX >> 4) + 1)); +} +#endif +@end + +@implementation RuntimeTestClass @synthesize foo = _foo; @synthesize bar = _bar; - (void)dealloc { @@ -56,74 +162,6 @@ { self = nil; return [self superTest]; } -@end - -@implementation TestsAppDelegate (RuntimeTests) -- (void)runtimeTests -{ - void *pool = objc_autoreleasePoolPush(); - RuntimeTest *test = [[[RuntimeTest alloc] init] autorelease]; - OFString *string, *foo; -#ifdef OF_OBJFW_RUNTIME - int classID; - uintmax_t value; - id object; -#endif - - EXPECT_EXCEPTION(@"Calling a non-existent method via super", - OFNotImplementedException, [test superTest]) - - TEST(@"Calling a method via a super with self == nil", - [test nilSuperTest] == nil) - - string = [OFMutableString stringWithString: @"foo"]; - foo = @"foo"; - - test.foo = string; - TEST(@"copy, nonatomic properties", [test.foo isEqual: foo] && - test.foo != foo && test.foo.retainCount == 1) - - test.bar = string; - TEST(@"retain, atomic properties", - test.bar == string && string.retainCount == 3) - - TEST(@"Associated objects", - R(objc_setAssociatedObject(self, testKey, test, - OBJC_ASSOCIATION_ASSIGN)) && test.retainCount == 2 && - R(objc_setAssociatedObject(self, testKey, test, - OBJC_ASSOCIATION_RETAIN)) && test.retainCount == 3 && - objc_getAssociatedObject(self, testKey) == test && - test.retainCount == 4 && - R(objc_setAssociatedObject(self, testKey, test, - OBJC_ASSOCIATION_ASSIGN)) && test.retainCount == 3 && - R(objc_setAssociatedObject(self, testKey, test, - OBJC_ASSOCIATION_RETAIN_NONATOMIC)) && test.retainCount == 4 && - objc_getAssociatedObject(self, testKey) == test && - test.retainCount == 4 && - R(objc_removeAssociatedObjects(self)) && test.retainCount == 3) - -#ifdef OF_OBJFW_RUNTIME - if (sizeof(uintptr_t) == 8) - value = 0xDEADBEEFDEADBEF; - else if (sizeof(uintptr_t) == 4) - value = 0xDEADBEF; - else - abort(); - - TEST(@"Tagged pointers", - objc_registerTaggedPointerClass([OFString class]) != -1 && - (classID = objc_registerTaggedPointerClass([OFNumber class])) != - -1 && - (object = objc_createTaggedPointer(classID, (uintptr_t)value)) && - object_getClass(object) == [OFNumber class] && - [object class] == [OFNumber class] && - object_getTaggedPointerValue(object) == value && - objc_createTaggedPointer(classID, UINTPTR_MAX >> 4) != nil && - objc_createTaggedPointer(classID, (UINTPTR_MAX >> 4) + 1) == nil) -#endif - - objc_autoreleasePoolPop(pool); -} @end DELETED tests/TestsAppDelegate.h Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ /dev/null @@ -1,297 +0,0 @@ -/* - * 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 "ObjFW.h" - -#define TEST(test, ...) \ - { \ - [self outputTesting: test inModule: module]; \ - \ - if (__VA_ARGS__) \ - [self outputSuccess: test inModule: module]; \ - else { \ - [self outputFailure: test inModule: module]; \ - _fails++; \ - } \ - } -#define EXPECT_EXCEPTION(test, exception, code) \ - { \ - bool caught = false; \ - \ - [self outputTesting: test inModule: module]; \ - \ - @try { \ - code; \ - } @catch (exception *e) { \ - caught = true; \ - } \ - \ - if (caught) \ - [self outputSuccess: test inModule: module]; \ - else { \ - [self outputFailure: test inModule: module]; \ - _fails++; \ - } \ - } -#define R(...) (__VA_ARGS__, 1) - -@class OFString; - -@interface TestsAppDelegate: OFObject -{ - int _fails; -} - -- (void)outputTesting: (OFString *)test inModule: (OFString *)module; -- (void)outputSuccess: (OFString *)test inModule: (OFString *)module; -- (void)outputFailure: (OFString *)test inModule: (OFString *)module; -@end - -@interface TestsAppDelegate (OFArrayTests) -- (void)arrayTests; -@end - -@interface TestsAppDelegate (OFBlockTests) -- (void)blockTests; -@end - -@interface TestsAppDelegate (OFCharacterSetTests) -- (void)characterSetTests; -@end - -@interface TestsAppDelegate (OFColorTests) -- (void)colorTests; -@end - -@interface TestsAppDelegate (OFDDPSocketTests) -- (void)DDPSocketTests; -@end - -@interface TestsAppDelegate (OFDNSResolverTests) -- (void)DNSResolverTests; -@end - -@interface TestsAppDelegate (OFDataTests) -- (void)dataTests; -@end - -@interface TestsAppDelegate (OFDateTests) -- (void)dateTests; -@end - -@interface TestsAppDelegate (OFDictionaryTests) -- (void)dictionaryTests; -@end - -@interface TestsAppDelegate (ForwardingTests) -- (void)forwardingTests; -@end - -@interface TestsAppDelegate (OFHTTPClientTests) -- (void)HTTPClientTests; -@end - -@interface TestsAppDelegate (OFHTTPCookieTests) -- (void)HTTPCookieTests; -@end - -@interface TestsAppDelegate (OFHTTPCookieManagerTests) -- (void)HTTPCookieManagerTests; -@end - -@interface TestsAppDelegate (OFINIFileTests) -- (void)INIFileTests; -@end - -@interface TestsAppDelegate (OFIRITests) -- (void)IRITests; -@end - -@interface TestsAppDelegate (OFIPXSocketTests) -- (void)IPXSocketTests; -@end - -@interface TestsAppDelegate (OFInvocationTests) -- (void)invocationTests; -@end - -@interface TestsAppDelegate (OFJSONTests) -- (void)JSONTests; -@end - -@interface TestsAppDelegate (OFHMACTests) -- (void)HMACTests; -@end - -@interface TestsAppDelegate (OFKernelEventObserverTests) -- (void)kernelEventObserverTests; -@end - -@interface TestsAppDelegate (OFListTests) -- (void)listTests; -@end - -@interface TestsAppDelegate (OFLocaleTests) -- (void)localeTests; -@end - -@interface TestsAppDelegate (OFMD5HashTests) -- (void)MD5HashTests; -@end - -@interface TestsAppDelegate (OFMatrix4x4Tests) -- (void)matrix4x4Tests; -@end - -@interface TestsAppDelegate (OFMemoryStreamTests) -- (void)memoryStreamTests; -@end - -@interface TestsAppDelegate (OFMethodSignatureTests) -- (void)methodSignatureTests; -@end - -@interface TestsAppDelegate (OFNotificationCenterTests) -- (void)notificationCenterTests; -@end - -@interface TestsAppDelegate (OFNumberTests) -- (void)numberTests; -@end - -@interface TestsAppDelegate (OFObjectTests) -- (void)objectTests; -@end - -@interface TestsAppDelegate (OFPBKDF2Tests) -- (void)PBKDF2Tests; -@end - -@interface TestsAppDelegate (OFPropertyListTests) -- (void)propertyListTests; -@end - -@interface TestsAppDelegate (OFPluginTests) -- (void)pluginTests; -@end - -@interface TestsAppDelegate (RuntimeTests) -- (void)runtimeTests; -@end - -@interface TestsAppDelegate (RuntimeARCTests) -- (void)runtimeARCTests; -@end - -@interface TestsAppDelegate (OFRIPEMD160HashTests) -- (void)RIPEMD160HashTests; -@end - -@interface TestsAppDelegate (OFScryptTests) -- (void)scryptTests; -@end - -@interface TestsAppDelegate (OFSHA1HashTests) -- (void)SHA1HashTests; -@end - -@interface TestsAppDelegate (OFSHA224HashTests) -- (void)SHA224HashTests; -@end - -@interface TestsAppDelegate (OFSHA256HashTests) -- (void)SHA256HashTests; -@end - -@interface TestsAppDelegate (OFSHA384HashTests) -- (void)SHA384HashTests; -@end - -@interface TestsAppDelegate (OFSHA512HashTests) -- (void)SHA512HashTests; -@end - -@interface TestsAppDelegate (OFSPXSocketTests) -- (void)SPXSocketTests; -@end - -@interface TestsAppDelegate (OFSPXStreamSocketTests) -- (void)SPXStreamSocketTests; -@end - -@interface TestsAppDelegate (OFSetTests) -- (void)setTests; -@end - -@interface TestsAppDelegate (OFSystemInfoTests) -- (void)systemInfoTests; -@end - -@interface TestsAppDelegate (OFSocketTests) -- (void)socketTests; -@end - -@interface TestsAppDelegate (OFStreamTests) -- (void)streamTests; -@end - -@interface TestsAppDelegate (OFStringTests) -- (void)stringTests; -@end - -@interface TestsAppDelegate (OFSubprocessTests) -- (void)subprocessTests; -@end - -@interface TestsAppDelegate (OFTCPSocketTests) -- (void)TCPSocketTests; -@end - -@interface TestsAppDelegate (OFThreadTests) -- (void)threadTests; -@end - -@interface TestsAppDelegate (OFUDPSocketTests) -- (void)UDPSocketTests; -@end - -@interface TestsAppDelegate (OFUNIXDatagramSocketTests) -- (void)UNIXDatagramSocketTests; -@end - -@interface TestsAppDelegate (OFUNIXStreamSocketTests) -- (void)UNIXStreamSocketTests; -@end - -@interface TestsAppDelegate (OFValueTests) -- (void)valueTests; -@end - -@interface TestsAppDelegate (OFWindowsRegistryKeyTests) -- (void)windowsRegistryKeyTests; -@end - -@interface TestsAppDelegate (OFXMLElementBuilderTests) -- (void)XMLElementBuilderTests; -@end - -@interface TestsAppDelegate (OFXMLNodeTests) -- (void)XMLNodeTests; -@end - -@interface TestsAppDelegate (OFXMLParserTests) - -- (void)XMLParserTests; -@end DELETED tests/TestsAppDelegate.m Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ /dev/null @@ -1,514 +0,0 @@ -/* - * 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" - -#include - -#import "TestsAppDelegate.h" - -#ifdef OF_IOS -# include -#endif - -#ifdef OF_PSP -# include -# include -# include -# include -PSP_MODULE_INFO("ObjFW Tests", 0, 0, 0); -#endif - -#ifdef OF_WII -# define asm __asm__ -# include -# include -# undef asm -#endif - -#ifdef OF_NINTENDO_DS -# define asm __asm__ -# include -# undef asm -#endif - -#ifdef OF_NINTENDO_3DS -/* Newer versions of libctru started using id as a parameter name. */ -# define id id_3ds -# include <3ds.h> -# undef id -#endif - -#ifdef OF_NINTENDO_SWITCH -# define id nx_id -# include -# undef id - -static OFDate *lastConsoleUpdate; - -static void -updateConsole(bool force) -{ - if (force || lastConsoleUpdate.timeIntervalSinceNow <= -1.0 / 60) { - consoleUpdate(NULL); - [lastConsoleUpdate release]; - lastConsoleUpdate = [[OFDate alloc] init]; - } -} -#endif - -extern unsigned long OFHashSeed; - -#ifdef OF_PSP -static int -exitCallback(int arg1, int arg2, void *arg) -{ - sceKernelExitGame(); - - return 0; -} - -static int -threadCallback(SceSize args, void *argp) -{ - sceKernelRegisterExitCallback( - sceKernelCreateCallback("Exit Callback", exitCallback, NULL)); - sceKernelSleepThreadCB(); - - return 0; -} -#endif - -int -main(int argc, char *argv[]) -{ -#ifdef OF_PSP - int tid; -#endif - -#if defined(OF_OBJFW_RUNTIME) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS) - /* - * This does not work on Win32 if ObjFW is built as a DLL. - * - * On AmigaOS, some destructors need to be able to send messages. - * Calling objc_deinit() via atexit() would result in the runtime being - * destructed before for the destructors ran. - */ - atexit(objc_deinit); -#endif - - /* We need deterministic hashes for tests */ - OFHashSeed = 0; - -#ifdef OF_WII - GXRModeObj *rmode; - void *xfb; - - VIDEO_Init(); - WPAD_Init(); - - rmode = VIDEO_GetPreferredMode(NULL); - xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); - VIDEO_Configure(rmode); - VIDEO_SetNextFramebuffer(xfb); - VIDEO_SetBlack(FALSE); - VIDEO_Flush(); - - VIDEO_WaitVSync(); - if (rmode->viTVMode & VI_NON_INTERLACE) - VIDEO_WaitVSync(); - - CON_InitEx(rmode, 10, 20, rmode->fbWidth - 10, rmode->xfbHeight - 20); - VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK); -#endif - -#ifdef OF_PSP - pspDebugScreenInit(); - - sceCtrlSetSamplingCycle(0); - sceCtrlSetSamplingMode(PSP_CTRL_MODE_DIGITAL); - - if ((tid = sceKernelCreateThread("update_thread", threadCallback, - 0x11, 0xFA0, 0, 0)) >= 0) - sceKernelStartThread(tid, 0, 0); -#endif - -#ifdef OF_NINTENDO_DS - consoleDemoInit(); -#endif - -#ifdef OF_NINTENDO_3DS - gfxInitDefault(); - atexit(gfxExit); - - consoleInit(GFX_TOP, NULL); -#endif - -#ifdef OF_NINTENDO_SWITCH - consoleInit(NULL); - padConfigureInput(1, HidNpadStyleSet_NpadStandard); - updateConsole(true); - -# ifdef OF_HAVE_FILES - [[OFFileManager defaultManager] changeCurrentDirectoryPath: @"romfs:/"]; -# endif -#endif - -#if defined(OF_WII) || defined(OF_WII_U) || defined(OF_PSP) || \ - defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) || \ - defined(OF_NINTENDO_SWITCH) - @try { - return OFApplicationMain(&argc, &argv, - [[TestsAppDelegate alloc] init]); - } @catch (id e) { - OFString *string = [OFString stringWithFormat: - @"\nRuntime error: Unhandled exception:\n%@\n", e]; - - [OFStdOut setForegroundColor: [OFColor red]]; - [OFStdOut writeString: string]; - - if ([e stackTraceAddresses] != nil) - [OFStdOut writeString: @"\nStack trace:\n"]; - - for (OFValue *address in [e stackTraceAddresses]) - [OFStdOut writeFormat: @" %p\n", address.pointerValue]; - -# if defined(OF_WII) - [OFStdOut reset]; - [OFStdOut writeString: @"Press home button to exit!"]; - - for (;;) { - WPAD_ScanPads(); - - if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) - [OFApplication terminateWithStatus: 1]; - - VIDEO_WaitVSync(); - } -# elif defined(OF_PSP) - sceKernelSleepThreadCB(); -# elif defined(OF_NINTENDO_DS) - [OFStdOut reset]; - [OFStdOut writeString: @"Press start button to exit!"]; - - for (;;) { - swiWaitForVBlank(); - scanKeys(); - if (keysDown() & KEY_START) - [OFApplication terminateWithStatus: 1]; - } -# elif defined(OF_NINTENDO_3DS) - [OFStdOut reset]; - [OFStdOut writeString: @"Press start button to exit!"]; - - for (;;) { - hidScanInput(); - - if (hidKeysDown() & KEY_START) - [OFApplication terminateWithStatus: 1]; - - gspWaitForVBlank(); - } -# elif defined(OF_NINTENDO_SWITCH) - while (appletMainLoop()) - updateConsole(true); - - consoleExit(NULL); - abort(); -# else - abort(); -# endif - } -#else - return OFApplicationMain(&argc, &argv, [[TestsAppDelegate alloc] init]); -#endif -} - -@implementation TestsAppDelegate -- (void)outputTesting: (OFString *)test inModule: (OFString *)module -{ - if (OFStdOut.hasTerminal) { - [OFStdOut setForegroundColor: [OFColor yellow]]; - [OFStdOut writeFormat: @"[%@] %@: testing...", module, test]; - } else - [OFStdOut writeFormat: @"[%@] %@: ", module, test]; - -#ifdef OF_NINTENDO_SWITCH - updateConsole(false); -#endif -} - -- (void)outputSuccess: (OFString *)test inModule: (OFString *)module -{ - if (OFStdOut.hasTerminal) { - [OFStdOut setForegroundColor: [OFColor lime]]; - [OFStdOut eraseLine]; - [OFStdOut writeFormat: @"\r[%@] %@: ok\n", module, test]; - } else - [OFStdOut writeLine: @"ok"]; - -#ifdef OF_NINTENDO_SWITCH - updateConsole(false); -#endif -} - -- (void)outputFailure: (OFString *)test inModule: (OFString *)module -{ - if (OFStdOut.hasTerminal) { - [OFStdOut setForegroundColor: [OFColor red]]; - [OFStdOut eraseLine]; - [OFStdOut writeFormat: @"\r[%@] %@: failed\n", module, test]; - } else - [OFStdOut writeLine: @"failed"]; - -#ifdef OF_WII - [OFStdOut reset]; - [OFStdOut writeLine: @"Press A to continue!"]; - - for (;;) { - WPAD_ScanPads(); - - if (WPAD_ButtonsDown(0) & WPAD_BUTTON_A) - return; - - VIDEO_WaitVSync(); - } -#endif -#ifdef OF_PSP - [OFStdOut reset]; - [OFStdOut writeLine: @"Press X to continue!"]; - - for (;;) { - SceCtrlData pad; - - sceCtrlReadBufferPositive(&pad, 1); - if (pad.Buttons & PSP_CTRL_CROSS) { - for (;;) { - sceCtrlReadBufferPositive(&pad, 1); - if (!(pad.Buttons & PSP_CTRL_CROSS)) - return; - } - } - } -#endif -#ifdef OF_NINTENDO_DS - [OFStdOut reset]; - [OFStdOut writeString: @"Press A to continue!"]; - - for (;;) { - swiWaitForVBlank(); - scanKeys(); - if (keysDown() & KEY_A) - break; - } -#endif -#ifdef OF_NINTENDO_3DS - [OFStdOut reset]; - [OFStdOut writeString: @"Press A to continue!"]; - - for (;;) { - hidScanInput(); - - if (hidKeysDown() & KEY_A) - break; - - gspWaitForVBlank(); - } -#endif -#ifdef OF_NINTENDO_SWITCH - [OFStdOut reset]; - [OFStdOut writeString: @"Press A to continue!"]; - - while (appletMainLoop()) { - PadState pad; - - padUpdate(&pad); - updateConsole(true); - - if (padGetButtonsDown(&pad) & HidNpadButton_A) - break; - } -#endif - - if (OFStdOut.hasTerminal) { - [OFStdOut writeString: @"\r"]; - [OFStdOut reset]; - [OFStdOut eraseLine]; - } -} - -- (void)applicationDidFinishLaunching: (OFNotification *)notification -{ -#if defined(OF_IOS) && defined(OF_HAVE_FILES) - CFBundleRef mainBundle = CFBundleGetMainBundle(); - CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); - UInt8 resourcesPath[PATH_MAX]; - - if (!CFURLGetFileSystemRepresentation(resourcesURL, true, resourcesPath, - PATH_MAX)) { - [OFStdErr writeString: @"Failed to locate resources!\n"]; - [OFApplication terminateWithStatus: 1]; - } - - [[OFFileManager defaultManager] changeCurrentDirectoryPath: - [OFString stringWithUTF8String: (const char *)resourcesPath]]; -#endif -#if defined(OF_WII) && defined(OF_HAVE_FILES) - [[OFFileManager defaultManager] - changeCurrentDirectoryPath: @"/apps/objfw-tests"]; -#endif - - [self runtimeTests]; -#ifdef COMPILER_SUPPORTS_ARC - [self runtimeARCTests]; -#endif - [self objectTests]; - [self methodSignatureTests]; - [self invocationTests]; - [self forwardingTests]; -#ifdef OF_HAVE_BLOCKS - [self blockTests]; -#endif - [self stringTests]; - [self characterSetTests]; - [self dataTests]; - [self arrayTests]; - [self dictionaryTests]; - [self listTests]; - [self setTests]; - [self dateTests]; - [self valueTests]; - [self numberTests]; - [self colorTests]; - [self streamTests]; - [self memoryStreamTests]; - [self notificationCenterTests]; - [self MD5HashTests]; - [self RIPEMD160HashTests]; - [self SHA1HashTests]; - [self SHA224HashTests]; - [self SHA256HashTests]; - [self SHA384HashTests]; - [self SHA512HashTests]; - [self HMACTests]; - [self PBKDF2Tests]; - [self scryptTests]; -#ifdef HAVE_CODEPAGE_437 - [self INIFileTests]; -#endif -#ifdef OF_HAVE_SOCKETS - [self socketTests]; - [self TCPSocketTests]; - [self UDPSocketTests]; -# ifdef OF_HAVE_UNIX_SOCKETS - [self UNIXDatagramSocketTests]; - [self UNIXStreamSocketTests]; -# endif -# ifdef OF_HAVE_IPX - [self IPXSocketTests]; - [self SPXSocketTests]; - [self SPXStreamSocketTests]; -# endif -# ifdef OF_HAVE_APPLETALK - [self DDPSocketTests]; -# endif - [self kernelEventObserverTests]; -#endif -#ifdef OF_HAVE_THREADS - [self threadTests]; -#endif - [self IRITests]; -#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) - [self HTTPClientTests]; -#endif -#ifdef OF_HAVE_SOCKETS - [self HTTPCookieTests]; - [self HTTPCookieManagerTests]; -#endif - [self XMLParserTests]; - [self XMLNodeTests]; - [self XMLElementBuilderTests]; - [self JSONTests]; - [self propertyListTests]; - [self matrix4x4Tests]; - -#ifdef OF_HAVE_PLUGINS - [self pluginTests]; -#endif -#ifdef OF_HAVE_SUBPROCESSES - [self subprocessTests]; -#endif -#ifdef OF_WINDOWS - [self windowsRegistryKeyTests]; -#endif - -#ifdef OF_HAVE_SOCKETS - [self DNSResolverTests]; -#endif - [self systemInfoTests]; - [self localeTests]; - - [OFStdOut reset]; - -#if defined(OF_IOS) - [OFStdOut writeFormat: @"%d tests failed!", _fails]; - [OFApplication terminateWithStatus: _fails]; -#elif defined(OF_WII) - [OFStdOut writeString: @"Press home button to exit!"]; - - for (;;) { - WPAD_ScanPads(); - - if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) - [OFApplication terminateWithStatus: _fails]; - - VIDEO_WaitVSync(); - } -#elif defined(OF_PSP) - [OFStdOut writeFormat: @"%d tests failed!", _fails]; - - sceKernelSleepThreadCB(); -#elif defined(OF_NINTENDO_DS) - [OFStdOut writeString: @"Press start button to exit!"]; - - for (;;) { - swiWaitForVBlank(); - scanKeys(); - if (keysDown() & KEY_START) - [OFApplication terminateWithStatus: _fails]; - } -#elif defined(OF_NINTENDO_3DS) - [OFStdOut writeString: @"Press start button to exit!"]; - - for (;;) { - hidScanInput(); - - if (hidKeysDown() & KEY_START) - [OFApplication terminateWithStatus: _fails]; - - gspWaitForVBlank(); - } -#elif defined(OF_NINTENDO_SWITCH) - while (appletMainLoop()) - updateConsole(true); - - consoleExit(NULL); - - [OFApplication terminateWithStatus: _fails]; -#else - [OFApplication terminateWithStatus: _fails]; -#endif -} -@end Index: tests/iOS.xcodeproj/project.pbxproj ================================================================== --- tests/iOS.xcodeproj/project.pbxproj +++ tests/iOS.xcodeproj/project.pbxproj @@ -5,10 +5,11 @@ }; objectVersion = 48; objects = { /* Begin PBXBuildFile section */ + 4B1E2CEF2B8294F200BB28B6 /* libobjfwtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B1E2CEE2B8294F200BB28B6 /* libobjfwtest.a */; }; 4B6AB9CD202BA431007BAC7D /* TestPlugin.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4B6AB9CA202BA408007BAC7D /* TestPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4BC7FD07201394F300280496 /* ObjFW.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC7FD06201394F300280496 /* ObjFW.framework */; }; 4BC7FD092013954B00280496 /* tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC7FD082013954B00280496 /* tests.a */; }; 4BC7FD0B2013956D00280496 /* ObjFW.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4BC7FD06201394F300280496 /* ObjFW.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4BC7FD102013960600280496 /* testfile.txt in Resources */ = {isa = PBXBuildFile; fileRef = 4BC7FD0C2013960600280496 /* testfile.txt */; }; @@ -39,10 +40,11 @@ runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4B1E2CEE2B8294F200BB28B6 /* libobjfwtest.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libobjfwtest.a; path = ../src/test/libobjfwtest.a; sourceTree = ""; }; 4B6AB9CA202BA408007BAC7D /* TestPlugin.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = TestPlugin.bundle; path = plugin/TestPlugin.bundle; sourceTree = ""; }; 4BC7FD06201394F300280496 /* ObjFW.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ObjFW.framework; path = ../src/ObjFW.framework; sourceTree = ""; }; 4BC7FD082013954B00280496 /* tests.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = tests.a; sourceTree = ""; }; 4BC7FD0C2013960600280496 /* testfile.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = testfile.txt; sourceTree = ""; }; 4BC7FD0D2013960600280496 /* testfile.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = testfile.bin; sourceTree = ""; }; @@ -55,12 +57,13 @@ /* Begin PBXFrameworksBuildPhase section */ 4BEBFB582013934E002E8710 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4BC7FD07201394F300280496 /* ObjFW.framework in Frameworks */, 4BC7FD092013954B00280496 /* tests.a in Frameworks */, + 4B1E2CEF2B8294F200BB28B6 /* libobjfwtest.a in Frameworks */, + 4BC7FD07201394F300280496 /* ObjFW.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ @@ -68,10 +71,11 @@ 4BC7FD05201394F300280496 /* Frameworks */ = { isa = PBXGroup; children = ( 4B6AB9CA202BA408007BAC7D /* TestPlugin.bundle */, 4BC7FD082013954B00280496 /* tests.a */, + 4B1E2CEE2B8294F200BB28B6 /* libobjfwtest.a */, 4BC7FD06201394F300280496 /* ObjFW.framework */, ); name = Frameworks; sourceTree = ""; }; @@ -302,10 +306,11 @@ INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", + "$(PROJECT_DIR)/../src/test", ); OTHER_LDFLAGS = "-all_load"; PRODUCT_BUNDLE_IDENTIFIER = im.nil.objfw.tests; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; @@ -322,10 +327,11 @@ INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", + "$(PROJECT_DIR)/../src/test", ); OTHER_LDFLAGS = "-all_load"; PRODUCT_BUNDLE_IDENTIFIER = im.nil.objfw.tests; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; Index: tests/testfile.ini ================================================================== --- tests/testfile.ini +++ tests/testfile.ini @@ -5,11 +5,11 @@ [foobar] ;foobarcomment qux=" asd" "quxqux " = asd -quxquxqux="hello\"wrld" +quxquxqux="hello\"wrld" qux2="a\f" [types] integer = 0x20 bool = true Index: utils/objfw-new/Makefile ================================================================== --- utils/objfw-new/Makefile +++ utils/objfw-new/Makefile @@ -1,10 +1,11 @@ include ../../extra.mk PROG = objfw-new${PROG_SUFFIX} SRCS = NewApp.m \ NewClass.m \ + NewTest.m \ ObjFWNew.m \ Property.m include ../../buildsys.mk ADDED utils/objfw-new/NewTest.m Index: utils/objfw-new/NewTest.m ================================================================== --- /dev/null +++ utils/objfw-new/NewTest.m @@ -0,0 +1,54 @@ +/* + * 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" + +#include + +#import "OFApplication.h" +#import "OFFile.h" +#import "OFStdIOStream.h" +#import "OFString.h" + +#import "OFOpenItemFailedException.h" + +void +newTest(OFString *name) +{ + OFString *path = [name stringByAppendingPathExtension: @"m"]; + OFFile *file = nil; + @try { + file = [OFFile fileWithPath: path mode: @"wx"]; + } @catch (OFOpenItemFailedException *e) { + if (e.errNo != EEXIST) + @throw e; + + [OFStdErr writeFormat: @"File %@ already exists! Aborting...\n", + e.path]; + [OFApplication terminateWithStatus: 1]; + } + + [file writeFormat: @"#import \n" + @"#import \n" + @"\n" + @"@interface %@: OTTestCase\n" + @"@end\n" + @"\n" + @"@implementation %@\n" + @"@end\n", + name, name]; + + [file close]; +} Index: utils/objfw-new/ObjFWNew.m ================================================================== --- utils/objfw-new/ObjFWNew.m +++ utils/objfw-new/ObjFWNew.m @@ -25,49 +25,53 @@ @interface ObjFWNew: OFObject @end extern void newApp(OFString *); extern void newClass(OFString *, OFString *, OFMutableArray *); +extern void newTest(OFString *); OF_APPLICATION_DELEGATE(ObjFWNew) static void help(OFStream *stream, bool full, int status) { [stream writeFormat: - @"Usage: %@ --app|--class [--superclass=] [--property=] name\n", + @"Usage: %@ --app|--class|--test [--superclass=] [--property=] name" + @"\n", [OFApplication programName]]; if (full) { [stream writeString: @"\n"]; [stream writeLine: @"Options:\n" @" -a --app Create a new app\n" @" -c --class Create a new class\n" @" -h --help Show this help\n" - @" -s --superclass= Specify the superclass for the " - @"class\n" @" -p --property= Add a property to the class.\n" @" E.g.: --property='(readonly, " - @"nonatomic) id foo'"]; + @"nonatomic) id foo'\n" + @" -s --superclass= Specify the superclass for the " + @"class\n" + @" -t --test Create a new test\n"]; } [OFApplication terminateWithStatus: status]; } @implementation ObjFWNew - (void)applicationDidFinishLaunching: (OFNotification *)notification { - bool app, class; + bool app, class, test; OFString *superclass = nil, *name; OFMutableArray OF_GENERIC(OFString *) *properties = nil; const OFOptionsParserOption options[] = { { 'a', @"app", 0, &app, NULL }, { 'c', @"class", 0, &class, NULL }, { 'h', @"help", 0, NULL, NULL }, - { 's', @"superclass", 1, NULL, &superclass }, { 'p', @"property", 1, NULL, NULL }, + { 's', @"superclass", 1, NULL, &superclass }, + { 't', @"test", 0, &test, NULL }, { '\0', nil, 0, NULL, NULL } }; OFOptionsParser *optionsParser; OFUnichar option; @@ -92,11 +96,12 @@ help(OFStdErr, false, 1); break; } } - if ((app ^ class) != 1 || optionsParser.remainingArguments.count != 1) + if (app + class + test != 1 || + optionsParser.remainingArguments.count != 1) help(OFStdErr, false, 1); if ((superclass && !class) || (properties != nil && !class)) help(OFStdErr, false, 1); @@ -108,11 +113,13 @@ if (app) newApp(name); else if (class) newClass(name, superclass, properties); + else if (test) + newTest(name); else help(OFStdErr, false, 1); [OFApplication terminate]; } @end