ObjFW  Check-in [fa3cda71d5]

Overview
Comment:Migrate all tests to ObjFWTest
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: fa3cda71d583fa932e49a90364abb850ce256b334cf99aafe56b6cc0999425ed
User & Date: js on 2024-02-18 20:05:21
Other Links: manifest | tags
Context
2024-02-18
20:34
A few minor improvements check-in: 4eea22291b user: js tags: trunk
20:05
Migrate all tests to ObjFWTest check-in: fa3cda71d5 user: js tags: trunk
19:57
Fix .fossil-settings/{clean,ignore}-glob Closed-Leaf check-in: 730de8fbf2 user: js tags: objfwtest
14:09
Update ChangeLog for 1.0.9 check-in: d080eb08fb user: js tags: trunk
Changes

Modified Makefile from [a04c0a2490] to [cf98c87f50].

13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27







-
+







include buildsys.mk

.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

release: docs
	echo "Generating tarball for version ${PACKAGE_VERSION}..."

Modified configure.ac from [fbf20e13cc] to [914db26e91].

735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
735
736
737
738
739
740
741

742
743
744
745
746
747
748







-







		@end
	], [[
		__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"

AC_C_BIGENDIAN([
1974
1975
1976
1977
1978
1979
1980
1981

1982
1983
1984
1985
1986
1987
1988
1973
1974
1975
1976
1977
1978
1979

1980
1981
1982
1983
1984
1985
1986
1987







-
+







			])
		], [
			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])
AC_CHECK_FUNCS(ioctl isatty)

Modified extra.mk.in from [3687ff7a31] to [5cc5b370f7].

82
83
84
85
86
87
88
89

90
91
92
93
94
82
83
84
85
86
87
88

89
90
91
92
93
94







-
+





UNICODE_M = @UNICODE_M@
USE_INCLUDES_ATOMIC = @USE_INCLUDES_ATOMIC@
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@

Modified src/Makefile from [faa3d6a6cb] to [cca3d2063b].

107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
107
108
109
110
111
112
113

114
115
116
117
118
119
120
121







-
+







       OFXMLParser.m			\
       OFXMLProcessingInstruction.m	\
       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
SRCS_SOCKETS = OFAAAADNSResourceRecord.m	\
	       OFADNSResourceRecord.m		\
153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
153
154
155
156
157
158
159

160
161
162
163
164
165
166
167







-
+







	       ${USE_SRCS_UNIX_SOCKETS}
SRCS_APPLETALK = OFDDPSocket.m
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		\
	       OFRecursiveMutex.m	\
	       OFTLSKey.m

Modified src/OFStdIOStream.m from [4bd5de9454] to [4e9d096bab].

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
115
116
117
118
119
120
121

122
123
124
125
126
127
128







-








	[OFStdErr writeFormat: @"[%@.%03d %@(%d)] %@\n", dateString,
			       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;
	if ([color isEqual: [OFColor maroon]])
		return 31;
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
153
154
155
156
157
158
159

160
161
162
163
164
165
166







-







	if ([color isEqual: [OFColor aqua]])
		return 96;
	if ([color isEqual: [OFColor white]])
		return 97;

	return -1;
}
#endif

@implementation OFStdIOStream
#ifndef OF_WINDOWS
+ (void)load
{
	if (self != [OFStdIOStream class])
		return;
428
429
430
431
432
433
434



435

436
437
438
439
440
441
442
426
427
428
429
430
431
432
433
434
435

436
437
438
439
440
441
442
443







+
+
+
-
+







- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}

- (bool)hasTerminal
{
#if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) || \
    defined(OF_NINTENDO_SWITCH)
	return true;
#if defined(HAVE_ISATTY) && !defined(OF_WII_U)
#elif defined(HAVE_ISATTY) && !defined(OF_WII_U)
	return isatty(_fd);
#else
	return false;
#endif
}

- (int)columns
467
468
469
470
471
472
473
474
475
476
477

478
479
480
481
482
483
484
485
486
487
488
489
490
491
492

493
494
495
496
497
498
499
500
501
502
503
504
505

506
507
508
509
510
511
512
513
514
515

516
517
518
519
520
521
522
523
524
525

526
527
528
529
530
531
532
533
534
535

536
537
538
539
540
541
542
543
544
545
546
547
548

549
550
551
552
553
554
555
556
557
558
559

560
561
562
563
564
565
566
567
568
569
570
571
572
573
468
469
470
471
472
473
474

475
476

477
478
479
480
481
482
483

484
485
486
487

488
489

490
491
492
493
494
495
496

497
498
499
500


501
502
503
504

505
506
507
508


509
510
511
512

513
514
515
516


517
518
519
520

521
522
523
524


525
526
527
528

529
530
531
532
533
534
535


536
537
538
539
540

541
542
543
544


545
546
547
548
549
550
551
552
553
554
555
556

557
558







-


-
+






-




-


-
+






-




-
-
+



-




-
-
+



-




-
-
+



-




-
-
+



-







-
-
+




-




-
-
+











-


#else
	return -1;
#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)
		[self writeFormat: @"\033[%uD", (unsigned)-position.x];

	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

Modified src/OFString+PathAdditions.m from [d839db3f93] to [1756dbb6cf].

17
18
19
20
21
22
23
24
25


26
27
28
29
17
18
19
20
21
22
23


24
25
26
27
28
29







-
-
+
+





#include "platform.h"

#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

Modified src/OFString.m from [855db5cb26] to [fd5aa1a3b0].

2337
2338
2339
2340
2341
2342
2343
2344
2345



2346
2347
2348



2349
2350
2351
2352
2353
2354
2355
2337
2338
2339
2340
2341
2342
2343


2344
2345
2346
2347
2348

2349
2350
2351
2352
2353
2354
2355
2356
2357
2358







-
-
+
+
+


-
+
+
+







	if ([stripped caseInsensitiveCompare: @"-NAN"] == OFOrderedSame)
		return -NAN;

#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;

	errno = 0;
2394
2395
2396
2397
2398
2399
2400
2401
2402



2403
2404
2405



2406
2407
2408
2409
2410
2411
2412
2397
2398
2399
2400
2401
2402
2403


2404
2405
2406
2407
2408

2409
2410
2411
2412
2413
2414
2415
2416
2417
2418







-
-
+
+
+


-
+
+
+







	if ([stripped caseInsensitiveCompare: @"-NAN"] == OFOrderedSame)
		return -NAN;

#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;

	errno = 0;

Modified src/test/Makefile from [b269c69e19] to [c3118bd230].

1
2
3
4
5
6
7


8
9
10
11
12


13
14
15
16
17
18
19
1
2
3
4
5
6

7
8
9
10
11
12

13
14
15
16
17
18
19
20
21






-
+
+




-
+
+







include ../../extra.mk

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

CPPFLAGS += -I. 			\
	    -I..			\

Deleted src/test/OTAppDelegate.h version [64bf862003].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23























-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <OFApplicationDelegate>
@end

OF_ASSUME_NONNULL_END

Modified src/test/OTAppDelegate.m from [3e448c799c] to [15d7c068c3].

11
12
13
14
15
16
17

18
19
20



21
22
23
24
25
26
27





















































28
29
30
31
32
33
34
35
36
37
38
39
40
41






















































42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

59

60
61
62
63
64
65
66
11
12
13
14
15
16
17
18



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176







+
-
-
-
+
+
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

















+

+







 * 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 "OFApplication.h"
#import "OTAppDelegate.h"

#import "OFColor.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 <CoreFoundation/CoreFoundation.h>
#endif

#ifdef OF_WII
# define asm __asm__
# include <gccore.h>
# include <wiiuse/wpad.h>
# undef asm
#endif

#ifdef OF_NINTENDO_DS
# define asm __asm__
# include <nds.h>
# 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 <switch.h>
# 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 <OFApplicationDelegate>
@end

enum Status {
	StatusRunning,
	StatusOk,
	StatusFailed,
	StatusSkipped
};

OF_APPLICATION_DELEGATE(OTAppDelegate)

static bool
isSubclassOfClass(Class class, Class superclass)
{
	for (Class iter = class; iter != Nil; iter = class_getSuperclass(iter))
		if (iter == superclass)
			return true;

	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;

	if (classes == NULL)
		return nil;

	@try {
		testClasses = [OFMutableSet set];

		for (Class *iter = classes; *iter != Nil; iter++) {
			/*
			 * 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.
			 */
			if (isSubclassOfClass(*iter, [OTTestCase class]))
85
86
87
88
89
90
91


92
93
94
95
96











97
98


99
100
101
102
103




104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120











121
122
123
124
125
126
127
128










129
130
131
132
133
134
135
136
137











138










139



140






141


































































































142
143
144
145
146
147


148








149
150


151
152


153
154
155
156
157
158
159
160
161

162
163
164
165
166

167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

184


















185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200

201

202
203



204





205



206

207
208
209

210

211
212

213


214
215
216
217
218
























219



220
221
222




223

224
225
226










227










































228
229
230
231
195
196
197
198
199
200
201
202
203
204
205
206
207

208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241







242
243
244
245
246
247
248
249
250
251
252
253







254
255
256
257
258
259
260
261
262
263
264








265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293
294
295
296

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399

400
401
402
403
404
405
406
407
408
409
410


411
412
413
414
415
416
417
418
419
420

421
422
423

424
425
426
427
428

429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445

446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480

481
482
483
484
485
486
487
488

489
490
491
492
493
494
495
496
497

498
499
500

501
502
503
504

505
506
507
508
509
510
511


512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541

542
543
544
545
546
547
548
549

550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606







+
+




-
+
+
+
+
+
+
+
+
+
+
+


+
+





+
+
+
+






-
+



-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
-
+
+
+

+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+
+

+
+
+
+
+
+
+
+
-
-
+
+


+
+




-



-
+




-
+
















-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+















-
+

+


+
+
+
-
+
+
+
+
+

+
+
+
-
+


-
+

+

-
+

+
+



-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+


-
+
+
+
+

+


-
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




		return nil;

	@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: @"]: "];
	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 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"];
	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 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];
	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 reset];
		[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 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: @"Running %zu test case(s)\n",
			       testClasses.count];
	[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 {
				[instance setUp];
				[instance performSelector: test.pointerValue];
			} @catch (OTAssertionFailedException *e) {
				/*
				 * If an assertion fails during -[setUp], don't
				 * run the test.
				 * 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) {
				/*
				 * If an assertion fails during -[tearDown],
				 * abort the tear down.
				 */
				if (!failed) {
					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

Modified src/test/OTAssert.h from [4079f8ef1f] to [5c08862032].

22
23
24
25
26
27
28




29
30
31
32
33
34
35
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39







+
+
+
+







#define OTAssert(cond, ...) \
	OTAssertImpl(self, _cmd, cond, @#cond, @__FILE__, __LINE__,	\
	    ## __VA_ARGS__, nil)
#define OTAssertTrue(cond, ...) OTAssert(cond == true, ## __VA_ARGS__)
#define OTAssertFalse(cond, ...) OTAssert(cond == false, ## __VA_ARGS__)
#define OTAssertEqual(a, b, ...) OTAssert(a == b, ## __VA_ARGS__)
#define OTAssertNotEqual(a, b, ...) OTAssert(a != b, ## __VA_ARGS__)
#define 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__)
#define OTAssertThrows(expression, ...)				\
	{							\
47
48
49
50
51
52
53


54
55
56
57
58
59

60
61
62
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69







+
+






+



		@try {						\
			expression;				\
		} @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

Modified src/test/OTAssert.m from [dfc3f7ba82] to [27e2adc076].

14
15
16
17
18
19
20

21
22
23
24
25
26
27
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28







+







 */

#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, ...)
{
	va_list arguments;
	OFConstantString *format;
39
40
41
42
43
44
45




















40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
			 arguments: arguments] autorelease];

	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 version [b6c6515436].
































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <ObjFW/ObjFW.h>
#endif

OF_ASSUME_NONNULL_BEGIN

@interface OTOrderedDictionary: OFDictionary
{
	OFArray *_keys;
	OFArray *_objects;
}
@end

OF_ASSUME_NONNULL_END

Added src/test/OTOrderedDictionary.m version [fea7c7d9b6].

























































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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

Modified src/test/OTTestCase.h from [2add704de5] to [a61157e757].

10
11
12
13
14
15
16
17

18
19

20
21
22
23
24






25
26
27
28
29
10
11
12
13
14
15
16

17
18

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35







-
+

-
+





+
+
+
+
+
+





 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#ifdef OBJFWTEST_LOCAL_INCLUDES
# import "OFObject.h"
# import "ObjFW.h"
#else
# import <ObjFW/OFObject.h>
# import <ObjFW/ObjFW.h>
#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

Modified src/test/OTTestCase.m from [3c3d45f200] to [fdb0a500c1].

14
15
16
17
18
19
20





21
22
23
24
25
26
27
28
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33







+
+
+
+
+








 */

#include "config.h"

#import "OTTestCase.h"

@implementation OTTestCase: OFObject
+ (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, id) *) *)summary
{
	return nil;
}

- (void)setUp
{
}

- (void)tearDown
{
}
@end

Added src/test/OTTestSkippedException.h version [499842bf2f].



































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [b2f5e97a93].




































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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

Modified src/test/ObjFWTest.h from [c6fa64803a] to [e2709fe53c].

11
12
13
14
15
16
17

11
12
13
14
15
16
17
18







+
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#import "OTTestCase.h"
#import "OTAssert.h"
#import "OTOrderedDictionary.h"

Modified tests/ForwardingTests.m from [1d203d1fb8] to [f769714063].

13
14
15
16
17
18
19
20


21
22
23
24
25
26


27

28
29
30
31
32
33
34
35
36

37
38
39

40
41
42
43
44
45
46
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38

39
40
41

42
43
44
45
46
47
48
49







-
+
+






+
+
-
+








-
+


-
+







 * file.
 */

#include "config.h"

#include <string.h>

#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"

@interface ForwardingTests: OTTestCase
@end
static OFString *const module = @"Forwarding";

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
				: (double)a3;
- (OFString *)forwardingTargetVarArgTest: (OFConstantString *)format, ...;
57
58
59
60
61
62
63
64











































































































































65
66
67
68
69
70
71
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212







-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








static void
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))) {
		class_replaceMethod(object_getClass(self), @selector(test),
		    (IMP)test, "v#:");
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
310
311
312
313
314
315
316
317





































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

	OFEnsure(self == target);

	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

Modified tests/Makefile from [f85e00f0ed] to [0e75cf4704].

1
2
3

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

20
21


22
23
24
25
26
27

























28
29
30

31
32

33
34
35
36

37
38
39
40
41
42
43





44
45
46
47
48
49
50
51
52
53
54
55
56
57
58










59
60
61


62
63
64
65
66
67
68






69
70
71
72
73
74


75
76
77
78
79
80
81



82
83
84
85
86
87
88

89
90
91
92
93
94
95
96
97
98
99
100
101
1
2

3



4
5
6
7
8
9
10
11
12
13
14
15
16
17


18
19






20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44



45


46




47







48
49
50
51
52















53
54
55
56
57
58
59
60
61
62



63
64







65
66
67
68
69
70
71
72
73
74


75
76
77
78
79
80



81
82
83
84
85
86
87
88
89

90
91
92
93



94
95
96
97
98
99
100


-
+
-
-
-













+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+




-
-
+
+




-
-
-
+
+
+






-
+



-
-
-







include ../extra.mk

SUBDIRS = ${TESTPLUGIN}	\
SUBDIRS = ${TESTPLUGIN} ${SUBPROCESS} ${OBJC_SYNC} terminal
	  ${OBJC_SYNC}	\
	  ${SUBPROCESS}	\
	  terminal

CLEAN = EBOOT.PBP		\
	boot.dol		\
	${PROG_NOINST}.arm9	\
	${PROG_NOINST}.nds	\
	${PROG_NOINST}.nro	\
	${PROG_NOINST}.rpx	\
	testfile_bin.m		\
	testfile_ini.m
DISTCLEAN = Info.plist

PROG_NOINST = tests${PROG_SUFFIX}
STATIC_LIB_NOINST = ${TESTS_STATIC_LIB}
SRCS = ${OF_BLOCK_TESTS_M}			\
SRCS = ForwardingTests.m		\
       OFArrayTests.m			\
       ForwardingTests.m			\
       OFArrayTests.m				\
       ${OF_BLOCK_TESTS_M}		\
       OFCharacterSetTests.m		\
       OFColorTests.m			\
       OFDataTests.m			\
       OFDateTests.m			\
       OFDictionaryTests.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		\
       OFHMACTests.m			\
       OFINIFileTests.m			\
       OFIRITests.m			\
       OFMutableSetTests.m			\
       OFInvocationTests.m		\
       OFJSONTests.m			\
       OFMutableStringTests.m			\
       OFListTests.m			\
       OFLocaleTests.m			\
       OFMD5HashTests.m			\
       OFMatrix4x4Tests.m		\
       OFMutableUTF8StringTests.m		\
       OFMemoryStreamTests.m		\
       OFMethodSignatureTests.m		\
       OFNotificationCenterTests.m	\
       OFNumberTests.m			\
       OFObjectTests.m			\
       OFPBKDF2Tests.m			\
       OFPropertyListTests.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		\
       OFScryptTests.m				\
       OFSetTests.m				\
       OFStreamTests.m				\
       OFStringTests.m				\
       OFSystemInfoTests.m			\
       OFUTF8StringTests.m			\
       OFValueTests.m				\
       OFXMLElementBuilderTests.m		\
       OFXMLNodeTests.m				\
       OFXMLParserTests.m			\
       RuntimeTests.m			\
       ${RUNTIME_ARC_TESTS_M}		\
       TestsAppDelegate.m		\
       ${RUNTIME_ARC_TESTS_M}			\
       RuntimeTests.m				\
       ${USE_SRCS_FILES}		\
       ${USE_SRCS_PLUGINS}		\
       ${USE_SRCS_SOCKETS}		\
       ${USE_SRCS_SUBPROCESS}		\
       ${USE_SRCS_THREADS}		\
       ${USE_SRCS_WINDOWS}		\
       testfile_bin.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	\
	       OFHTTPCookieManagerTests.m	\
	       OFHTTPCookieTests.m		\
	       OFKernelEventObserverTests.m	\
	       OFSocketTests.m			\
	       OFTCPSocketTests.m		\
	       OFUDPSocketTests.m		\
	       ${USE_SRCS_APPLETALK}		\
	       ${USE_SRCS_IPX}			\
	       ${USE_SRCS_UNIX_SOCKETS}
	       ${USE_SRCS_IPX}			\
	       ${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
	${SHELL} ../utils/objfw-embed testfile.ini testfile.ini $@

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
151
152
153
154
155
156
157


















158
159
160
161
162
163
164







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







	rm -f libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
	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}; \
	fi
	if test -f ../src/runtime/libobjfwrt.so; then \
198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
179
180
181
182
183
184
185

186
187
188
189
190
191
192
193







-
+







	mksfo "ObjFW Tests" PARAM.SFO
	psp-strip ${PROG_NOINST}
	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}
	arm-none-eabi-objcopy -O binary $< $@

228
229
230
231
232
233
234

235
236

237
238





239





240
241
209
210
211
212
213
214
215
216
217

218
219
220
221
222
223
224
225

226
227
228
229
230
231
232







+

-
+


+
+
+
+
+
-
+
+
+
+
+



${PROG_NOINST}.rpx: ${PROG_NOINST}
	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
# 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	\
LIBS := ${TESTS_LIBS} ${LIBS}
	${TESTS_LIBS}	\
	${LIBS}		\
	-lobjfwtest	\
	${TESTS_LIBS}	\
	${LIBS}
LDFLAGS += ${MAP_LDFLAGS}
LD = ${OBJC}

Added tests/OFArrayTests.h version [8f8b5c5038].


























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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

Modified tests/OFArrayTests.m from [1024ac5260] to [805893a2b1].

11
12
13
14
15
16
17
18

19





20

21
22
23
24
25
26





27


28

29




30
31
32
33
34
35














36
37
38
39
40










































41




42
43
44
45
46
47
48
49
50
51
52
53
54





























































55

56




















































57


58
59
60
61
62
63
64
65
66





































67

68
















69
70
71
72
73
74
75


76
77
78
79
80
81
82
11
12
13
14
15
16
17

18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35
36

37
38
39
40

41
42
43
44
45





46
47
48
49
50
51
52
53
54
55
56
57
58
59
60




61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107













108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225









226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286


287
288
289
290
291
292
293
294
295







-
+

+
+
+
+
+
-
+






+
+
+
+
+
-
+
+

+
-
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
+
+







 * 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 "TestsAppDelegate.h"
#import "OFArrayTests.h"

@interface CustomArray: OFArray
{
	OFArray *_array;
}
@end
static OFString *module;

static OFString *const cArray[] = {
	@"Foo",
	@"Bar",
	@"Baz"
};

@implementation OFArrayTests
- (Class)arrayClass
{
	return [CustomArray class];
}
@interface SimpleArray: OFArray

- (void)setUp
{
	[super setUp];
	OFMutableArray *_array;

	_array = [[self.arrayClass alloc]
	    initWithObjects: cArray
		      count: sizeof(cArray) / sizeof(*cArray)];
}
@end

@interface SimpleMutableArray: OFMutableArray
{
	OFMutableArray *_array;

- (void)dealloc
{
	[_array release];

	[super dealloc];
}

- (void)testArray
{
	OFArray *array = [self.arrayClass array];

	OTAssertNotNil(array);
	OTAssertEqual(array.count, 0);
}
@end

@implementation SimpleArray
- (instancetype)init

- (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]);
}
	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

- (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;
	self = [super init];

	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);
}
	@try {
		_array = [[OFMutableArray alloc] initWithObject: object
						      arguments: arguments];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;

- (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;
	}

	return self;
}
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
306
307
308
309
310
311
312
313














































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
	return [_array objectAtIndex: idx];
}

- (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

Modified tests/OFBlockTests.m from [41100ed0e1] to [b2a3854f7c].

11
12
13
14
15
16
17
18


19
20


21
22
23
24
25
26
27
11
12
13
14
15
16
17

18
19
20

21
22
23
24
25
26
27
28
29







-
+
+

-
+
+







 * 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 "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;
#elif defined(OF_APPLE_RUNTIME)
extern void *_NSConcreteStackBlock;
52
53
54
55
56
57
58
59
60
61
62


































































63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

86
87
88
89
90
91
92
93
94
95
96
97
98

99
100
101
102
103
104

105
106

107
108
109
110
111
112
113
114
115
116
117
118
119
54
55
56
57
58
59
60




61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

133















134













135






136


137












138







-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-


	block();
	Block_release(block);

	return d;
}

@implementation TestsAppDelegate (OFBlockTests)
- (void)blockTests
{
	void *pool = objc_autoreleasePoolPush();
@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
{
	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 = [[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])
	OTAssertEqual([[mallocBlock copy] autorelease], (id)mallocBlock);
#endif

#ifndef __clang_analyzer__
	TEST(@"Copying a malloc block",
	    (id)mallocBlock == [mallocBlock copy] &&
	    [mallocBlock retainCount] == 2)
	OTAssertEqual([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);
}
@end

Modified tests/OFCharacterSetTests.m from [cbaf41131b] to [2ac90291b5].

11
12
13
14
15
16
17
18


19
20
21
22
23
24


25
26

27
28
29

30
31
32
33
34
35
36
37


38
39
40

41
42
43
44
45

46
47
48
49
50
51
52





53
54

55
56
57
58
59
60
61
62







63
64
65
66
67



68
69
70
71









72
73

74
75
76
77
78
79

80
81
82
83
84
85
86





87
88
89
90
91
92
93
94
95
96
97












98
99
100
101




102
103


104
105

106
107
11
12
13
14
15
16
17

18
19
20
21
22
23
24

25
26
27

28
29
30

31
32
33
34
35
36
37


38
39
40


41





42
43






44
45
46
47
48


49

50






51
52
53
54
55
56
57
58




59
60
61




62
63
64
65
66
67
68
69
70


71






72
73






74
75
76
77
78











79
80
81
82
83
84
85
86
87
88
89
90




91
92
93
94


95
96
97

98
99
100







-
+
+





-
+
+

-
+


-
+






-
-
+
+

-
-
+
-
-
-
-
-
+

-
-
-
-
-
-
+
+
+
+
+
-
-
+
-

-
-
-
-
-
-
+
+
+
+
+
+
+

-
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
+

-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
+
+

-
+


 * 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 "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
@implementation OFCharacterSetTests
- (void)testCustomCharacterSet
{
	void *pool = objc_autoreleasePoolPush();
	OFCharacterSet *characterSet, *invertedCharacterSet;
	OFCharacterSet *characterSet =
	bool ok;

	module = @"OFCharacterSet";

	characterSet = [[[SimpleCharacterSet alloc] init] autorelease];
	    [[[CustomCharacterSet 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])
	for (OFUnichar c = 0; c < 65536; c++)
		if (c % 2 == 0)
			OTAssertTrue([characterSet characterIsMember: c]);
		else
			OTAssertFalse([characterSet characterIsMember: c]);
			ok = false;
	}
}
	TEST(@"-[characterIsMember:]", ok);

	module = @"OFBitSetCharacterSet";

	TEST(@"+[characterSetWithCharactersInString:]",
	    (characterSet = [OFCharacterSet characterSetWithCharactersInString:
	    @"0123456789"]) &&
	    [characterSet isKindOfClass: [OFBitSetCharacterSet class]])
- (void)testBitSetCharacterSet
{
	OFCharacterSet *characterSet =
	    [OFCharacterSet characterSetWithCharactersInString: @"0123456789"];

	OTAssertTrue(
	    [characterSet isKindOfClass: [OFBitSetCharacterSet class]]);

	ok = true;
	for (OFUnichar c = 0; c < 65536; c++) {
		if (c >= '0' && c <= '9') {
			if (![characterSet characterIsMember: c])
	for (OFUnichar c = 0; c < 65536; c++)
		if (c >= '0' && c <= '9')
			OTAssertTrue([characterSet characterIsMember: c]);
				ok = false;
		} else if ([characterSet characterIsMember: c])
			ok = false;
	}
		else if ([characterSet characterIsMember: c])
			OTAssertFalse([characterSet characterIsMember: c]);
}

- (void)testRangeCharacterSet
{
	OFCharacterSet *characterSet =
	    [OFCharacterSet characterSetWithRange: OFMakeRange('0', 10)];

	TEST(@"-[characterIsMember:]", ok);

	OTAssertTrue(
	module = @"OFRangeCharacterSet";

	TEST(@"+[characterSetWithRange:]",
	    (characterSet = [OFCharacterSet
	    characterSetWithRange: OFMakeRange('0', 10)]) &&
	    [characterSet isKindOfClass: [OFRangeCharacterSet class]])
	    [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])
	for (OFUnichar c = 0; c < 65536; c++)
		if (c >= '0' && c <= '9')
			OTAssertTrue([characterSet characterIsMember: c]);
		else
			OTAssertFalse([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])
}

- (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]);
			ok = false;
	}
	TEST(@"-[invertedSet]", ok);

}

- (void)testInvertingInvertedSetReturnsOriginal
{
	TEST(@"Inverting -[invertedSet] returns original set",
	    invertedCharacterSet.invertedSet == characterSet)
	OFCharacterSet *characterSet =
	    [OFCharacterSet characterSetWithRange: OFMakeRange('0', 10)];

	objc_autoreleasePoolPop(pool);
	OTAssertEqual(characterSet, characterSet.invertedSet.invertedSet);
}
@end

Modified tests/OFColorTests.m from [43c0fcb4d4] to [f2116a7a9c].

11
12
13
14
15
16
17
18


19


20
21
22
23






24
25
26

27
28
29
30
31
32
33
34













35
36
37




38
39



40
41
42
43
44






45
46
47
11
12
13
14
15
16
17

18
19
20
21
22




23
24
25
26
27
28
29


30

31






32
33
34
35
36
37
38
39
40
41
42
43
44
45


46
47
48
49
50
51
52
53
54





55
56
57
58
59
60

61
62







-
+
+

+
+
-
-
-
-
+
+
+
+
+
+

-
-
+
-

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+


+
+
+
-
-
-
-
-
+
+
+
+
+
+
-


 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFColorTests: OTTestCase
{
static OFString *const module = @"OFColor";

@implementation TestsAppDelegate (OFColorTests)
- (void)colorTests
	OFColor *_color;
}
@end

@implementation OFColorTests
- (void)setUp
{
	void *pool = objc_autoreleasePoolPush();
	OFColor *color;
	[super setUp];
	float red, green, blue, alpha;

	TEST(@"+[colorWithRed:green:blue:alpha:]",
	    (color = [OFColor colorWithRed: 63.f / 255
				     green: 127.f / 255
				      blue: 1
				     alpha: 1]))

	_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

- (void)testGetRedGreenBlueAlpha
{
	float red, green, blue, alpha;
	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)


	[_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);
	objc_autoreleasePoolPop(pool);
}
@end

Added tests/OFConcreteArrayTests.m version [70542429e0].































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [59f7bc08f5].































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [b38f014238].










































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [5d0492a10a].





































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [b0e6f39f4d].


































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [8b35e67bcd].































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [9a95ece37d].













































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <string.h>

#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 <OFCryptographicHash> hash =
	    [hashClass hashWithAllowsSwappableMemory: true];
	id <OFCryptographicHash> 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

Modified tests/OFDDPSocketTests.m from [5433dc7a16] to [d03a653eed].

12
13
14
15
16
17
18

19
20


21
22


23
24
25


26
27
28
29
30
31
32

33
34
35
36
37
38
39




40
41
42
43
44
45
46
47

48
49
50
51
52
53
54

55
56
57
58
59
60

61
62
63

64
65
66

67
68
69
70
71
72
73








74
75
76
12
13
14
15
16
17
18
19
20

21
22
23

24
25
26


27
28
29

30
31
32
33

34
35
36





37
38
39
40
41
42
43
44




45


46




47


48
49
50

51



52



53
54






55
56
57
58
59
60
61
62


63







+

-
+
+

-
+
+

-
-
+
+

-




-
+


-
-
-
-
-
+
+
+
+




-
-
-
-
+
-
-

-
-
-
-
+
-
-



-
+
-
-
-
+
-
-
-
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-

 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <errno.h>
#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFDDPSocket";
@interface OFDDPSocketTests: OTTestCase
@end

@implementation TestsAppDelegate (OFDDPSocketTests)
- (void)DDPSocketTests
@implementation OFDDPSocketTests
- (void)testDDPSocket
{
	void *pool = objc_autoreleasePoolPush();
	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 "
			OTSkip(@"AppleTalk unsupported");
			    @"tests"];
			break;
		case EADDRNOTAVAIL:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFDDPSocket] -[bindToNetwork:node:port:"
			    @"protocolType:] AppleTalk not configured, "
			OTSkip(@"AppleTalk not configured");
			    @"skipping tests"];
			break;
		default:
			@throw e;
		}

	}
		objc_autoreleasePoolPop(pool);
		return;
	}


	TEST(@"-[sendBuffer:length:receiver:]",
	    R([sock sendBuffer: "Hello" length: 5 receiver: &address1]))
	[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))

	OTAssertEqual([sock receiveIntoBuffer: buffer
				       length: 5
				       sender: &address2], 5);
	OTAssertEqual(memcmp(buffer, "Hello", 5), 0);
	OTAssertTrue(OFSocketAddressEqual(&address1, &address2));
	OTAssertEqual(OFSocketAddressHash(&address1),
	    OFSocketAddressHash(&address2));
}
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFDNSResolverTests.m from [979544c90c] to [09e6956470].

11
12
13
14
15
16
17
18


19
20
21





22
23

24
25
26

27








28
29
30
31
32
33
34
35
36
37
38
39
40

41
42
43
44


45
46
47
48


49
50

51
52
53

54
55
56

57
58
59
60


61
62
63


64
65
66
67




68
69

70
71
11
12
13
14
15
16
17

18
19
20


21
22
23
24
25
26

27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

52

53


54
55




56
57


58
59


60



61




62
63



64
65
66



67
68
69
70
71

72
73
74







-
+
+

-
-
+
+
+
+
+

-
+



+
-
+
+
+
+
+
+
+
+












-
+
-

-
-
+
+
-
-
-
-
+
+
-
-
+

-
-
+
-
-
-
+
-
-
-
-
+
+
-
-
-
+
+

-
-
-
+
+
+
+

-
+


 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@implementation TestsAppDelegate (OFDNSResolverTests)
- (void)DNSResolverTests
@interface OFDNSResolverTests: OTTestCase
@end

@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];

#define ADD(name, value)						\
	[OFStdOut setForegroundColor: [OFColor lime]];
	[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)
			[staticHosts appendString: @"; "];

		IPs = [[resolver.staticHosts objectForKey: host]
		    componentsJoinedByString: @", "];

		[staticHosts appendFormat: @"%@=(%@)", host, IPs];
	}
	[OFStdOut writeFormat: @"[OFDNSResolver] Static hosts: %@\n",
	ADD(@"Static hosts", staticHosts)
	    staticHosts];

	[OFStdOut writeFormat: @"[OFDNSResolver] Name servers: %@\n",
	    [resolver.nameServers componentsJoinedByString: @", "]];
	ADD(@"Name servers",
	    [resolver.nameServers componentsJoinedByString: @", "]);

	[OFStdOut writeFormat: @"[OFDNSResolver] Local domain: %@\n",
	    resolver.localDomain];

	ADD(@"Local domain", resolver.localDomain);
	ADD(@"Search domains",
	[OFStdOut writeFormat: @"[OFDNSResolver] Search domains: %@\n",
	    [resolver.searchDomains componentsJoinedByString: @", "]];
	    [resolver.searchDomains componentsJoinedByString: @", "]);

	[OFStdOut writeFormat: @"[OFDNSResolver] Timeout: %lf\n",
	    resolver.timeout];
	ADD_DOUBLE(@"Timeout", resolver.timeout);

	[OFStdOut writeFormat: @"[OFDNSResolver] Max attempts: %u\n",
	    resolver.maxAttempts];
	ADD_UINT(@"Max attempts", resolver.maxAttempts);

	[OFStdOut writeFormat:
	    @"[OFDNSResolver] Min number of dots in absolute name: %u\n",
	    resolver.minNumberOfDotsInAbsoluteName];
	ADD_UINT(@"Min number of dots in absolute name",
	    resolver.minNumberOfDotsInAbsoluteName);

	[OFStdOut writeFormat: @"[OFDNSResolver] Forces TCP: %u\n",
	    resolver.forcesTCP];
	ADD_BOOL(@"Forces TCP", resolver.forcesTCP);
	ADD_DOUBLE(@"Config reload interval", resolver.configReloadInterval);

	[OFStdOut writeFormat:
	    @"[OFDNSResolver] Config reload interval: %lf\n",
	    resolver.configReloadInterval];
#undef ADD
#undef ADD_DOUBLE
#undef ADD_UINT
#undef ADD_BOOL

	objc_autoreleasePoolPop(pool);
	return summary;
}
@end

Added tests/OFDataTests.h version [b0edcdb3e6].



























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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

Modified tests/OFDataTests.m from [a8658a6bbf] to [e445376d45].

13
14
15
16
17
18
19
20

21



22
23
24
25




26
27
28
29
30
31




32
33
34
35
36
37
38
39
40
41
42
43



















44
45
46
47
48
49
50
51
52











53
54
55
56
57
58
59









60
61
62
63
64
65










66
67
68
69
70
71
72
73
74
















75
76
77
78
79






80
81
82







83
84
85
86
87
88
89
90
91















92
93
94
95


96
97
98
99



100
101
102
103


104
105
106
107



108
109
110
111


112
113
114
115



116
117
118
119


120
121
122
123
124
125
126
127







128
129
130
131



132
133
134
135
136
137
138
139
140

















141
142
143
144



145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161




























162
163
164
165
166
167
168













169
170
171
172






173
174
175
176






177
178
179
180




181
182
183

184
185

186
187
188
189
190




191
192
193
194
195
196




197
198
199
200
201
202
203
204








205
206
207


208
209
210
211
212
213
214
215
216










217
218
219



220
221
222
223


224
225
226





227
228
13
14
15
16
17
18
19

20
21
22
23
24




25
26
27
28
29





30
31
32
33
34











35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53









54
55
56
57
58
59
60
61
62
63
64







65
66
67
68
69
70
71
72
73






74
75
76
77
78
79
80
81
82
83









84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99





100
101
102
103
104
105



106
107
108
109
110
111
112









113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129


130
131
132



133
134
135
136
137


138
139
140



141
142
143
144
145


146
147
148



149
150
151
152
153


154
155
156







157
158
159
160
161
162
163
164



165
166
167
168
169







170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186




187
188
189
190


191














192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220






221
222
223
224
225
226
227
228
229
230
231
232
233




234
235
236
237
238
239




240
241
242
243
244
245




246
247
248
249



250


251





252
253
254
255






256
257
258
259








260
261
262
263
264
265
266
267



268
269









270
271
272
273
274
275
276
277
278
279



280
281
282




283
284



285
286
287
288
289
290
291







-
+

+
+
+
-
-
-
-
+
+
+
+

-
-
-
-
-
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+

-
-
-
+
+
+


-
-
+
+

-
-
-
+
+
+


-
-
+
+

-
-
-
+
+
+


-
-
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
-
-
+
+
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+

-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
+
-
-
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
+
+
-
-
-
+
+
+
+
+


 * file.
 */

#include "config.h"

#include <string.h>

#import "TestsAppDelegate.h"
#import "OFDataTests.h"

@implementation OFDataTests
- (Class)dataClass
{
static OFString *const module = @"OFData";

@implementation TestsAppDelegate (OFDataTests)
- (void)dataTests
	return [OFData class];
}

- (void)setUp
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableData *mutableData;
	OFData *data;
	void *raw[2];
	OFRange range;
	[super setUp];

	memset(&_items[0], 0xFF, 4096);
	memset(&_items[1], 0x42, 4096);

	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]]))

	_data = [[self.dataClass alloc] initWithItems: _items
						count: 2
					     itemSize: 4096];
}

- (void)dealloc
{
	[_data release];

	[super dealloc];
}

- (void)testCount
{
	OTAssertEqual(_data.count, 2);
}

- (void)testItemSize
{
	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:]",
	OTAssertEqual(_data.itemSize, 4096);
}

- (void)testItems
{
	OTAssertEqual(memcmp(_data.items, _items, 2 * _data.itemSize), 0);
}

- (void)testItemAtIndex
{
	OTAssertEqual(
	    (data = [OFData dataWithItems: mutableData.items
				    count: mutableData.count
				 itemSize: mutableData.itemSize]) &&
	    [data isEqual: mutableData] &&
	    R([mutableData removeLastItem]) && ![mutableData isEqual: data])

	TEST(@"-[mutableCopy]",
	    memcmp([_data itemAtIndex: 1], &_items[1], _data.itemSize), 0);
}

- (void)testItemAtIndexThrowsOnOutOfRangeIndex
{
	OTAssertThrowsSpecific([_data itemAtIndex: _data.count],
	    OFOutOfRangeException);
}

	    (mutableData = [[data mutableCopy] autorelease]) &&
	    [mutableData isEqual: data])

	TEST(@"-[compare]", [mutableData compare: data] == 0 &&
	    R([mutableData removeLastItem]) &&
	    [data compare: mutableData] == OFOrderedDescending &&
- (void)testFirstItem
{
	OTAssertEqual(memcmp(_data.firstItem, &_items[0], _data.itemSize), 0);
}

- (void)testLastItem
{
	OTAssertEqual(memcmp(_data.lastItem, &_items[1], _data.itemSize), 0);
}

	    [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]",
- (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]);
}

	    R([mutableData removeLastItem]) && mutableData.count == 5 &&
	    memcmp(mutableData.items, "abcde", 5) == 0)

	TEST(@"-[removeItemsInRange:]",
	    R([mutableData removeItemsInRange: OFMakeRange(1, 2)]) &&
- (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];

	    mutableData.count == 3 && memcmp(mutableData.items, "ade", 3) == 0)

	TEST(@"-[insertItems:atIndex:count:]",
	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);
}

	    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]
- (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)];
	TEST(@"-[rangeOfData:options:range:] #1",
	    range.location == 0 && range.length == 1)
	OTAssertEqual(range.location, 0);
	OTAssertEqual(range.length, 1);

	range = [data rangeOfData: [OFData dataWithItems: "aa"
						   count: 1
						itemSize: 2]
	range = [data rangeOfData: [self.dataClass dataWithItems: "aa"
							   count: 1
							itemSize: 2]
			  options: OFDataSearchBackwards
			    range: OFMakeRange(0, 7)];
	TEST(@"-[rangeOfData:options:range:] #2",
	    range.location == 5 && range.length == 1)
	OTAssertEqual(range.location, 5);
	OTAssertEqual(range.length, 1);

	range = [data rangeOfData: [OFData dataWithItems: "ac"
						   count: 1
						itemSize: 2]
	range = [data rangeOfData: [self.dataClass dataWithItems: "ac"
							   count: 1
							itemSize: 2]
			  options: 0
			    range: OFMakeRange(0, 7)];
	TEST(@"-[rangeOfData:options:range:] #3",
	    range.location == 2 && range.length == 1)
	OTAssertEqual(range.location, 2);
	OTAssertEqual(range.length, 1);

	range = [data rangeOfData: [OFData dataWithItems: "aabb"
						   count: 2
						itemSize: 2]
	range = [data rangeOfData: [self.dataClass dataWithItems: "aabb"
							   count: 2
							itemSize: 2]
			  options: 0
			    range: OFMakeRange(0, 7)];
	TEST(@"-[rangeOfData:options:range:] #4",
	    range.location == 5 && range.length == 2)
	OTAssertEqual(range.location, 5);
	OTAssertEqual(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: [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: [OFData dataWithItems: "aa"
						   count: 1
						itemSize: 2]
	range = [data rangeOfData: [self.dataClass 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"
	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]
					       count: 1
					    itemSize: 3]
		      options: 0
			range: OFMakeRange(0, 1)])
		       options: 0
			 range: OFMakeRange(1, 2)],
	    OFOutOfRangeException);

	EXPECT_EXCEPTION(
	    @"-[rangeOfData:options:range:] failing on out of range",
	OTAssertThrowsSpecific(
	    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)])
	    [_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];

	EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #2",
	    OFOutOfRangeException,
	    [mutableData subdataWithRange: OFMakeRange(6, 1)])

	TEST(@"-[stringByMD5Hashing]",
	    [mutableData.stringByMD5Hashing
	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,
	    isEqual: @"ab56b4d92b40713acc5af89985d4b786"])

	TEST(@"-[stringByRIPEMD160Hashing]",
	    [mutableData.stringByRIPEMD160Hashing
	    @"37d65c8816008d58175b1d71ee892de3");
}

- (void)testStringByRIPEMD160Hashing
{
	OTAssertEqualObjects(_data.stringByRIPEMD160Hashing,
	    isEqual: @"973398b6e6c6cfa6b5e6a5173f195ce3274bf828"])

	TEST(@"-[stringBySHA1Hashing]",
	    [mutableData.stringBySHA1Hashing
	    @"ab33a6a725f9fcec6299054dc604c0eb650cd889");
}

- (void)testStringBySHA1Hashing
{
	OTAssertEqualObjects(_data.stringBySHA1Hashing,
	    isEqual: @"03de6c570bfe24bfc328ccd7ca46b76eadaf4334"])

	TEST(@"-[stringBySHA224Hashing]",
	    [mutableData.stringBySHA224Hashing
	    @"eb50cfcc29d0bed96b3bafe03e99110bcf6663b3");
}

- (void)testStringBySHA224Hashing
	    isEqual: @"bdd03d560993e675516ba5a50638b6531ac2ac3d5847c61916cfced6"
	    ])

{
	TEST(@"-[stringBySHA256Hashing]",
	    [mutableData.stringBySHA256Hashing
	OTAssertEqualObjects(_data.stringBySHA224Hashing,
	    isEqual: @"36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0"
		     @"c44ca42c"])

	TEST(@"-[stringBySHA384Hashing]",
	    [mutableData.stringBySHA384Hashing
	    @"204f8418a914a6828f8eb27871e01f74366f6d8fac8936029ebf0041");
}

- (void)testStringBySHA256Hashing
	    isEqual: @"4c525cbeac729eaf4b4665815bc5db0c84fe6300068a727cf74e2813"
		     @"521565abc0ec57a37ee4d8be89d097c0d2ad52f0"])

	TEST(@"-[stringBySHA512Hashing]",
	    [mutableData.stringBySHA512Hashing
	    isEqual: @"878ae65a92e86cac011a570d4c30a7eaec442b85ce8eca0c2952b5e3"
{
	OTAssertEqualObjects(_data.stringBySHA256Hashing,
	    @"27c521859f6f5b10aeac4e210a6d005c"
	    @"85e382c594e2622af9c46c6da8906821");
		     @"cc0628c2e79d889ad4d5c7c626986d452dd86374b6ffaa7cd8b67665"
		     @"bef2289a5c70b0a1"])

	TEST(@"-[stringByBase64Encoding]",
	    [mutableData.stringByBase64Encoding isEqual: @"YWJjZGU="])

	TEST(@"+[dataWithBase64EncodedString:]",
	    memcmp([[OFData dataWithBase64EncodedString: @"YWJjZGU="] items],
}

- (void)testStringBySHA384Hashing
{
	OTAssertEqualObjects(_data.stringBySHA384Hashing,
	    @"af99a52c26c00f01fe649dcc53d7c7a0"
	    @"a9ee0150b971955be2af395708966120"
	    @"5f2634f70df083ef63b232d5b8549db4");
	    "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,
- (void)testStringBySHA512Hashing
{
	OTAssertEqualObjects(_data.stringBySHA512Hashing,
	    @"1cbd53bf8bed9b45a63edda645ee1217"
	    @"24d2f0323c865e1039ba13320bc6c66e"
	    @"c79b6cdf6d08395c612b7decb1e59ad1"
	    @"e72bfa007c2f76a823d10204d47d2e2d");
}

- (void)testStringByBase64Encoding
	    [mutableData addItems: raw[0] count: SIZE_MAX])

	EXPECT_EXCEPTION(@"Detect out of range in -[removeItemsInRange:]",
{
	OTAssertEqualObjects([[self.dataClass dataWithItems: "abcde" count: 5]
	    stringByBase64Encoding], @"YWJjZGU=");
	    OFOutOfRangeException,
	    [mutableData removeItemsInRange: OFMakeRange(mutableData.count, 1)])

	OFFreeMemory(raw[0]);
}

	OFFreeMemory(raw[1]);

	objc_autoreleasePoolPop(pool);
- (void)testDataWithBase64EncodedString
{
	OTAssertEqualObjects(
	    [self.dataClass dataWithBase64EncodedString: @"YWJjZGU="],
	    [OFData dataWithItems: "abcde" count: 5]);
}
@end

Modified tests/OFDateTests.m from [a09d2c22e2] to [f4f700dfaa].

13
14
15
16
17
18
19
20



21
22


23
24
25
26















27


28
29
30






31
32
33
34
35
36
37
38











39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
























56
57
58
59
60
61
62
63











64
65
66
67
68
69
70
71










72
73


74
75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
















































































111
112
13
14
15
16
17
18
19

20
21
22
23
24
25
26




27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44



45
46
47
48
49
50
51







52
53
54
55
56
57
58
59
60
61
62


63














64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88







89
90
91
92
93
94
95
96
97
98
99
100







101
102
103
104
105
106
107
108
109
110
111

112
113
114


115
116

































117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198







-
+
+
+


+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
-
-
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
+
+

-
-
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


 * file.
 */

#include "config.h"

#include <time.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

#import "OFStrPTime.h"

@interface OFDateTests: OTTestCase
{
static OFString *const module = @"OFDate";

@implementation TestsAppDelegate (OFDateTests)
- (void)dateTests
	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];
	void *pool = objc_autoreleasePoolPush();
	OFDate *date1, *date2;


	[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)

	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);
}
	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,
- (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"]);
				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");

	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,
	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 version [e83688b5f9].


























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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

Modified tests/OFDictionaryTests.m from [322f36dbf5] to [49b4f5c917].

11
12
13
14
15
16
17
18

19
20
21
22
23
24
25

26
27
28
29
30

31
32

33
34
35





36


37









38
39



40
41
42
43
44









































45





46
47
48
49
50
51
52
53
54
55
56























































57




58


59


60
61
62
63
64
65
66
67
68
69



























































70









71




72
73













74

75
76
77
78
79
80
81
82
83
84



85
86
87
88
89
90
91
11
12
13
14
15
16
17

18
19

20
21
22
23

24
25
26
27
28

29
30

31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47
48
49
50
51


52
53
54
55




56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102











103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162

163
164
165
166
167










168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262




263
264
265
266
267
268
269
270
271
272







-
+

-




-
+




-
+

-
+



+
+
+
+
+
-
+
+

+
+
+
+
+
+
+
+
+
-
-
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
-
+
+

+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
-
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
-
+






-
-
-
-
+
+
+







 * 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 "TestsAppDelegate.h"
#import "OFDictionaryTests.h"

static OFString *module;
static OFString *keys[] = {
	@"key1",
	@"key2"
};
static OFString *values[] = {
static OFString *objects[] = {
	@"value1",
	@"value2"
};

@interface SimpleDictionary: OFDictionary
@interface CustomDictionary: OFDictionary
{
	OFMutableDictionary *_dictionary;
	OFDictionary *_dictionary;
}
@end

@implementation OFDictionaryTests
- (Class)dictionaryClass
{
	return [CustomDictionary class];
}
@interface SimpleMutableDictionary: OFMutableDictionary

- (void)setUp
{
	[super setUp];

	_dictionary = [[self.dictionaryClass alloc] initWithObjects: objects
							    forKeys: keys
							      count: 2];
}

- (void)dealloc
{
	OFMutableDictionary *_dictionary;
	unsigned long _mutations;
	[_dictionary release];

	[super dealloc];
}
@end

@implementation SimpleDictionary
- (instancetype)init

- (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]);
}
	self = [super init];

	@try {
		_dictionary = [[OFMutableDictionary alloc] init];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (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]]));
}
- (instancetype)initWithKey: (id)key arguments: (va_list)arguments

- (void)testObjectEnumerator
{
	OFEnumerator *enumerator = [_dictionary objectEnumerator];
	OFString *first, *second;
	self = [super init];

	@try {
		_dictionary = [[OFMutableDictionary alloc]
		    initWithKey: key
		      arguments: arguments];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	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 self;
		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
- (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;
	}

	return self;
}
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
288
289
290
291
292
293
294
295


















































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
	return _dictionary.count;
}

- (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

Modified tests/OFHMACTests.m from [b03d7ee96c] to [726dca6e51].

13
14
15
16
17
18
19
20


21





22

23
24
25
26
27
28
29
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35







-
+
+

+
+
+
+
+
-
+







 * file.
 */

#include "config.h"

#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFHMACTests: OTTestCase
{
	OFStream *_stream;
}
@end
static OFString *const module = @"OFHMAC";

static const uint8_t key[] =
    "yM9h8K6IWnJRvxC/0F8XRWG7RnACDBz8wqK2tbXrYVLoKC3vPLeJikyJSM47tVHc"
    "DlXHww9zULAC2sJUlm2Kg1z4oz2aXY3Y1PQSB4VkC/m0DQ7hCI6cAg4TWnKdzWTy"
    "cvYGX+Y6HWeDY79/PGSd8fNItme6I8w4HDBqU7BP2sum3jbePJqoiSnhcyJZQTeZ"
    "jw0ZXoyrfHgOYD2M+NsTDaGpLblFtQ7n5CczjKtafG40PkEwx1dcrd46U9i3GyTK";
static const size_t keyLength = sizeof(key);
static const uint8_t MD5Digest[] =
43
44
45
46
47
48
49
50
51


52

53
54
55





56
57
58
59


60
61
62
63
64
65











66
67
68



69
70
71
72
73
74
75







76
77
78
79


80
81
82
83

84
85
86
87
88
89
90
91
92
93
94
95

96
97
98
99
100
101
102
103
104
105
106
















107
108
109
110




111
112
113


114
115
116
117
118
119
120
121
122
123
124
125
126
127

















128


129
130
49
50
51
52
53
54
55


56
57
58
59



60
61
62
63
64




65
66






67
68
69
70
71
72
73
74
75
76
77



78
79
80







81
82
83
84
85
86
87

88


89
90

91


92










93

94
95










96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111




112
113
114
115



116
117














118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134

135
136
137
138







-
-
+
+

+
-
-
-
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-

-
-
+
+
-

-
-
+
-
-
-
-
-
-
-
-
-
-

-
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+


    "\x14\x52\x33\x4B\x9C\x5F\xD8\x07\x4E\x98\xAE\x97\x46\x29\x24\xB4";
static const uint8_t SHA512Digest[] =
    "\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
@implementation OFHMACTests
- (void)setUp
{
	OFIRI *IRI;
	void *pool = objc_autoreleasePoolPush();
	OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"];
	OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"];

	[super setUp];

	IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"];
	_stream = [[OFIRIHandler openItemAtIRI: IRI mode: @"r"] retain];
	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",
- (void)tearDown
{
	[_stream close];

	[super tearDown];
}

- (void)dealloc
{
	[_stream release];

	    (HMACRMD160 = [OFHMAC HMACWithHashClass: [OFRIPEMD160Hash class]
			      allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with SHA-256",
	[super dealloc];
}

	    (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]
- (void)testWithHashClass: (Class)hashClass
	   expectedDigest: (const unsigned char *)expectedDigest
{
	OFHMAC *HMAC = [OFHMAC HMACWithHashClass: hashClass
			   allowsSwappableMemory: true];

	OTAssertNotNil(HMAC);
			      allowsSwappableMemory: true]))

	EXPECT_EXCEPTION(@"Detection of missing key",
	    OFInvalidArgumentException,
	OTAssertThrowsSpecific([HMAC updateWithBuffer: "" length: 0],
	    OFInvalidArgumentException);
	    [HMACMD5 updateWithBuffer: "" length: 0])

	TEST(@"-[setKey:length:] with MD5",
	    R([HMACMD5 setKey: key length: keyLength]))
	[HMAC 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) {
	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];

		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
{
	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]))
	[self testWithHashClass: [OFRIPEMD160Hash class]
		 expectedDigest: RIPEMD160Digest];
}

	TEST(@"-[calculate] with SHA-384", R([HMACSHA384 calculate]))
	TEST(@"-[calculate] with SHA-512", R([HMACSHA512 calculate]))

- (void)testHMACWithSHA1
{
	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)

	[self testWithHashClass: [OFSHA1Hash class] expectedDigest: SHA1Digest];
}

- (void)testHMACWithSHA256
{
	[self testWithHashClass: [OFSHA256Hash class]
		 expectedDigest: SHA256Digest];
}

- (void)testHMACWithSHA384
{
	[self testWithHashClass: [OFSHA384Hash class]
		 expectedDigest: SHA384Digest];
}

- (void)testHMACWithSHA512
{
	objc_autoreleasePoolPop(pool);
	[self testWithHashClass: [OFSHA512Hash class]
		 expectedDigest: SHA512Digest];
}
@end

Modified tests/OFHTTPClientTests.m from [8ae0f89264] to [d3dab1d884].

14
15
16
17
18
19
20
21


22
23

24
25
26



27
28
29
30
31
32

33
34
35
36
37
38
39
40
41
42
43

44
45

46
47
48
49
50

51
52
53
54
55

56
57
58
59
60
61

62
63
64
65

66
67

68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

96

97

98
99
100
101
102

103
104
105
106
107
108

109
110


111
112


113
114
115
116
117
118
119


120
121
122
123

124
125
126
127
128
129
130
131








132
133
134























135
136
137
138
139
140




























141



142
143
144
145
146



























147


148













149
150
14
15
16
17
18
19
20

21
22
23

24



25
26
27

28
29
30
31

32
33
34

35







36


37





38


39


40






41




42


43


44







45

46

47

48
49
50
51
52
53
54
55
56
57
58
59

60
61
62

63
64
65
66
67

68
69

70
71

72
73
74
75
76
77


78
79
80


81
82


83
84
85
86
87

88
89







90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123






124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155





156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200







-
+
+

-
+
-
-
-
+
+
+
-




-
+


-

-
-
-
-
-
-
-
+
-
-
+
-
-
-
-
-
+
-
-

-
-
+
-
-
-
-
-
-
+
-
-
-
-
+
-
-
+
-
-

-
-
-
-
-
-
-
+
-

-

-












-
+

+
-
+




-
+

-


-

+


+
+
-
-
+
+

-
-


-
-
+
+



-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+


 */

#include "config.h"

#include <inttypes.h>
#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFHTTPClient";
@interface OFHTTPClientTests: OTTestCase <OFHTTPClientDelegate>
static OFCondition *condition;
static OFHTTPResponse *response = nil;

{
	OFHTTPResponse *_response;
}
@interface TestsAppDelegate (HTTPClientTests) <OFHTTPClientDelegate>
@end

@interface HTTPClientTestsServer: OFThread
{
@public
	OFCondition *_condition;
	uint16_t _port;
}
@end

@implementation HTTPClientTestsServer
- (id)main
{
	OFTCPSocket *listener, *client;
	OFSocketAddress address;
	char buffer[5];

@property (readonly, nonatomic) OFCondition *condition;
	[condition lock];

@property (readonly) uint16_t port;
	listener = [OFTCPSocket socket];
	address = [listener bindToHost: @"127.0.0.1" port: 0];
	_port = OFSocketAddressIPPort(&address);
	[listener listen];

@end
	[condition signal];
	[condition unlock];

	client = [listener accept];

@implementation OFHTTPClientTests
	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"]);

- (void)dealloc
	if (![[client readLine] isEqual:
	    [OFString stringWithFormat: @"Host: 127.0.0.1:%" @PRIu16, _port]])
		OFEnsure(0);

{
	OFEnsure([[client readLine] isEqual: @""]);

	[_response release];
	[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];

	[super dealloc];
	return nil;
}
@end

@implementation TestsAppDelegate (OFHTTPClientTests)
-     (void)client: (OFHTTPClient *)client
  wantsRequestBody: (OFStream *)body
	   request: (OFHTTPRequest *)request
{
	[body writeString: @"Hello"];
}

-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (OFHTTPResponse *)response_
	  exception: (id)exception
{
	OFEnsure(exception == nil);
	OTAssertNil(exception);

	[_response release];
	response = [response_ retain];
	_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;

	server = [[[HTTPClientTestsServer alloc] init] autorelease];
	server.supportsSockets = true;
	condition = [OFCondition condition];
	[condition lock];

	[server.condition lock];

	server = [[[HTTPClientTestsServer alloc] init] autorelease];
	server.supportsSockets = true;
	[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]];
					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]))
	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]];

	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) {
	[response autorelease];

	TEST(@"Asynchronous handling of requests", response != nil)

	TEST(@"Normalization of server header keys",
	    [response.headers objectForKey: @"Content-Length"] != nil)
		[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];
	TEST(@"Correct parsing of data",
	    (data = [response readDataUntilEndOfStream]) &&
	    data.count == 7 && memcmp(data.items, "foo\nbar", 7) == 0)

	[server join];

	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";
	objc_autoreleasePoolPop(pool);

	[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

Modified tests/OFHTTPCookieManagerTests.m from [fdaaae41cf] to [03be30a247].

11
12
13
14
15
16
17
18


19
20


21
22
23


24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43


44
45
46
47
48
49

50
51
52
53
54



55
56
57
58
59
60
61

62
63
64
65
66



67
68
69
70
71
72
73
74

75
76
77
78
79
80




81
82
83
84
85
86

87
88
89
90
91
92




93
94
95
96
97
98
99
100






101
102
103
11
12
13
14
15
16
17

18
19
20

21
22
23


24
25
26

27
28
29
30
31
32
33
34
35
36
37
38


39




40
41
42
43
44
45


46





47
48
49
50
51
52
53
54


55





56
57
58
59
60
61
62
63
64


65






66
67
68
69
70
71
72
73


74






75
76
77
78
79







80
81
82
83
84
85


86







-
+
+

-
+
+

-
-
+
+

-












-
-
+
-
-
-
-
+
+




-
-
+
-
-
-
-
-
+
+
+





-
-
+
-
-
-
-
-
+
+
+






-
-
+
-
-
-
-
-
-
+
+
+
+




-
-
+
-
-
-
-
-
-
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
-
-

 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFHTTPCookieManager";
@interface OFHTTPCookieManagerTests: OTTestCase
@end

@implementation TestsAppDelegate (OFHTTPCookieManagerTests)
- (void)HTTPCookieManagerTests
@implementation OFHTTPCookieManagerTests
- (void)testCookieManager
{
	void *pool = objc_autoreleasePoolPush();
	OFHTTPCookieManager *manager = [OFHTTPCookieManager manager];
	OFIRI *IRI1, *IRI2, *IRI3, *IRI4;
	OFHTTPCookie *cookie1, *cookie2, *cookie3, *cookie4, *cookie5;

	IRI1 = [OFIRI IRIWithString: @"http://nil.im/foo"];
	IRI2 = [OFIRI IRIWithString: @"https://nil.im/foo/bar"];
	IRI3 = [OFIRI IRIWithString: @"https://test.nil.im/foo/bar"];
	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]))
	[manager addCookie: cookie1 forIRI: IRI1];

	TEST(@"-[cookiesForIRI:] #1",
	    [[manager cookiesForIRI: IRI1] isEqual:
	    [OFArray arrayWithObject: cookie1]])
	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]))
	[manager addCookie: cookie2 forIRI: IRI1];

	TEST(@"-[cookiesForIRI:] #2",
	    [[manager cookiesForIRI: IRI1] isEqual:
	    [OFArray arrayWithObject: cookie1]] &&
	    [[manager cookiesForIRI: IRI4] isEqual: [OFArray array]])
	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]))
	[manager addCookie: cookie3 forIRI: IRI2];

	TEST(@"-[cookiesForIRI:] #3",
	    [[manager cookiesForIRI: IRI2] isEqual:
	    [OFArray arrayWithObject: cookie3]] &&
	    [[manager cookiesForIRI: IRI1] isEqual: [OFArray array]])
	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]))
	[manager addCookie: cookie4 forIRI: IRI2];

	TEST(@"-[cookiesForIRI:] #4",
	    [[manager cookiesForIRI: IRI2] isEqual:
	    [OFArray arrayWithObject: cookie4]] &&
	    [[manager cookiesForIRI: IRI3] isEqual:
	    [OFArray arrayWithObject: cookie4]])
	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]))
	[manager addCookie: cookie5 forIRI: IRI1];

	TEST(@"-[cookiesForIRI:] #5",
	    [[manager cookiesForIRI: IRI1] isEqual:
	    [OFArray arrayWithObject: cookie4]] &&
	    [[manager cookiesForIRI: IRI3] isEqual:
	    [OFArray arrayWithObjects: cookie4, cookie5, nil]])
	OTAssertEqualObjects([manager cookiesForIRI: IRI1],
	    [OFArray arrayWithObject: cookie4]);
	OTAssertEqualObjects([manager cookiesForIRI: IRI3],
	    ([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]])

	OTAssertEqualObjects(manager.cookies,
	    ([OFArray arrayWithObjects: cookie3, cookie4, cookie5, nil]));
	[manager purgeExpiredCookies];
	OTAssertEqualObjects(manager.cookies,
	    ([OFArray arrayWithObjects: cookie4, cookie5, nil]));
}
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFHTTPCookieTests.m from [eb0c618283] to [ef1c0d2e2f].

11
12
13
14
15
16
17
18


19
20


21
22
23


24
25
26
27

28
29
30
31
32
33
34
35
36
37












38
39
40
41
42
43
44
45
46





47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67






























68
69
70


71
72
73
11
12
13
14
15
16
17

18
19
20

21
22
23


24
25
26

27

28










29
30
31
32
33
34
35
36
37
38
39
40
41








42
43
44
45
46
47
48
49
50
51
52
53
54
55
56











57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87


88
89


90







-
+
+

-
+
+

-
-
+
+

-

-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+










-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
-
-

 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFHTTPCookie";
@interface OFHTTPCookieTests: OTTestCase
@end

@implementation TestsAppDelegate (OFHTTPCookieTests)
- (void)HTTPCookieTests
@implementation OFHTTPCookieTests
- (void)testCookiesWithResponseHeaderFieldsForIRI
{
	void *pool = objc_autoreleasePoolPush();
	OFIRI *IRI = [OFIRI IRIWithString: @"http://nil.im"];
	OFHTTPCookie *cookie1, *cookie2;
	OFHTTPCookie *cookie1 = [OFHTTPCookie cookieWithName: @"foo"
	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]])
						       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]);

	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]])
	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";
	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:

	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"]])

					forKey: @"Cookie"]);
}
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFINIFileTests.m from [793add569b] to [878892df10].

11
12
13
14
15
16
17
18


19
20
21
22
23








24

25
26














































































27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49




50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76



77
78
79
80
81
82

83
84
85
86
87
88

89
90
91
92
93
94

95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110

111
112
113
114


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131












132
133

134
135

136
137
138
11
12
13
14
15
16
17

18
19
20




21
22
23
24
25
26
27
28
29
30


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127




128
129
130
131
132
133
134
135























136
137
138






139






140






141






142










143
144



145
146


147
148
149
150
151
152
153
154







155
156
157
158
159
160
161
162
163
164
165
166
167

168
169

170


171







-
+
+

-
-
-
-
+
+
+
+
+
+
+
+

+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



















-
-
-
-
+
+
+
+




-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+

-
-
-
+
+
-
-








-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
+

-
+
-
-

 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *module;

@implementation TestsAppDelegate (OFINIFileTests)
- (void)INIFileTests
@interface OFINIFileTests: OTTestCase
{
	OFINIFile *_file;
}
@end

@implementation OFINIFileTests
- (void)setUp
{
	OFIRI *IRI;
	void *pool = objc_autoreleasePoolPush();
	OFString *output = @"[tests]\r\n"

	[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"
	    @"[foobar]\r\n"
	    @";foobarcomment\r\n"
	    @"qux=\" asd\"\r\n"
	    @"quxquxqux=\"hello\\\"wörld\"\r\n"
	    @"qux2=\"a\\f\"\r\n"
	    @"qux3=a\fb\r\n"
	    @"\r\n"
	    @"[types]\r\n"
	    @"integer=16\r\n"
	    @"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"]))
	[tests setStringValue: @"baz" forKey: @"foo"];
	[tests setStringValue: @"new" forKey: @"new"];
	[foobar setStringValue: @"a\fb" forKey: @"qux3"];

	TEST(@"-[longLongValueForKey:defaultValue:]",
	    [types longLongValueForKey: @"integer" defaultValue: 2] == 0x20)

	TEST(@"-[setLongLongValue:forKey:]",
	    R([types setLongLongValue: 0x10 forKey: @"integer"]))
	[types setLongLongValue: 0x10 forKey: @"integer"];

	TEST(@"-[boolValueForKey:defaultValue:]",
	    [types boolValueForKey: @"bool" defaultValue: false] == true)

	TEST(@"-[setBoolValue:forKey:]",
	    R([types setBoolValue: false forKey: @"bool"]))
	[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"]))
	[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"]))
	[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"]))
	[types setArrayValue: array forKey: @"array1"];

	TEST(@"-[removeValueForKey:]",
	    R([foobar removeValueForKey: @"quxqux "]) &&
	    R([types removeValueForKey: @"array2"]))
	[foobar removeValueForKey: @"quxqux "];
	[types removeValueForKey: @"array2"];

	module = @"OFINIFile";

	/* 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

Modified tests/OFIPXSocketTests.m from [0ae4e44d89] to [1646dd280e].

12
13
14
15
16
17
18

19
20


21
22


23
24
25


26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43




44
45
46
47
48
49
50

51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
12
13
14
15
16
17
18
19
20

21
22
23

24
25
26


27
28
29
30
31


32
33
34
35
36
37


38





39
40
41
42
43
44
45




46

47




48


49
50
51



52
53
54
55
56
57
58







+

-
+
+

-
+
+

-
-
+
+

+

-
-






-
-

-
-
-
-
-
+
+
+
+



-
-
-
-
+
-

-
-
-
-
+
-
-



-
-
-







 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <errno.h>
#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFIPXSocket";
@interface OFIPXSocketTests: OTTestCase
@end

@implementation TestsAppDelegate (OFIPXSocketTests)
- (void)IPXSocketTests
@implementation OFIPXSocketTests
- (void)testIPXSocket
{
	OFIPXSocket *sock = [OFIPXSocket socket];
	const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
	void *pool = objc_autoreleasePoolPush();
	OFIPXSocket *sock;
	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"];
			OTSkip(@"IPX unsupported");
			break;
		case EADDRNOTAVAIL:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFIPXSocket] -[bindToNetwork:node:port:"
			    @"packetType:]: IPX not configured, skipping "
			OTSkip(@"IPX not configured");
			    @"tests"];
			break;
		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.
	 */
	networkInterfaces = [OFSystemInfo networkInterfaces];
82
83
84
85
86
87
88
89

90
91
92
93

94
95
96

97
98
99

100
101
102
103
104
105
106
107
108
109










110
111
112
69
70
71
72
73
74
75

76




77



78



79
80









81
82
83
84
85
86
87
88
89
90


91







-
+
-
-
-
-
+
-
-
-
+
-
-
-
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-

		    OFSocketAddressIPXNetwork([addresses itemAtIndex: 0]));
		OFSocketAddressGetIPXNode([addresses itemAtIndex: 0], node);
		OFSocketAddressSetIPXNode(&address1, node);
	}

	OFSocketAddressGetIPXNode(&address1, node);
	if (OFSocketAddressIPXNetwork(&address1) == 0 &&
	    memcmp(node, zeroNode, 6) == 0) {
	    memcmp(node, zeroNode, 6) == 0)
		[OFStdOut setForegroundColor: [OFColor lime]];
		[OFStdOut writeLine:
		    @"[OFIPXSocket] -[sendBuffer:length:receiver:]: "
		    @"Could not determine own address, skipping tests"];
		OTSkip(@"Could not determine own IPX address");
		objc_autoreleasePoolPop(pool);
		return;
	}


	TEST(@"-[sendBuffer:length:receiver:]",
	    R([sock sendBuffer: "Hello" length: 5 receiver: &address1]))
	[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))

	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));
}
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFIRITests.m from [3036a831f2] to [55f0cf3dc6].

11
12
13
14
15
16
17
18


19






20
21


22
23
24
25


26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43














44


45
46
47







48
49
50
51
52
53
54







55
56
57
58
59





60
61
62
63





64
65
66


67
68
69
70
71





72
73
74


75
76
77


78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99



































100
101
102
103


104
105
106



107
108
109
110
111



112
113
114
115
116
117








118
119
120
121
122
123
124








125
126
127
128
129
130






131
132
133
134
135
136
137
138
139
140
141
142
143
















144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163


























































































164
165
166
167
168
169
170
171

172
173
174

175
176
177
178
179
180
181
182
183
184
185








186
187
188
189
190
191
192
193
194
195
196



197
198
199
200
201
202
203
204
205
206
207
208
















209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227




































228
229

230
231

232
233

234
235
236
237
238
239
240
241

















242
243
244


245
246
247
248
249
250
251
252








253
254
255
256
257
258
259
260
261











262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279























280
281
282
283
284
285
286
287
288
289
290
291
292



















293
294
295
296
297
298
299
300
301
302
303
304
305


















306
307
308
309
310
311
312
313
314
315
316
317
318


















319
320
321
322





323
324

325
326
327
328
329
330
331
332
333
334
335
336
337
338





















339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367












































368
369
370
371
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26


27
28
29
30


31
32
33

34



35












36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52



53
54
55
56
57
58
59
60






61
62
63
64
65
66
67

68



69
70
71
72
73
74



75
76
77
78
79
80


81
82

83



84
85
86
87
88
89


90
91



92
93






















94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128




129
130



131
132
133

134



135
136
137

138




139
140
141
142
143
144
145
146
147
148





149
150
151
152
153
154
155
156
157





158
159
160
161
162
163
164
165
166










167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185

















186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276







277



278











279
280
281
282
283
284
285
286











287
288
289












290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306


















307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342


343


344


345








346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362



363
364
365







366
367
368
369
370
371
372
373
374








375
376
377
378
379
380
381
382
383
384
385

386
















387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409













410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428













429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446













447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464




465
466
467
468
469
470
471
472














473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493





























494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537


538
539







-
+
+

+
+
+
+
+
+
-
-
+
+


-
-
+
+

-
+
-
-
-

-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
-
-
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
-

-
-
-
+
+
+
+
+

-
-
-
+
+
+
+
+

-
-
+
+
-

-
-
-
+
+
+
+
+

-
-
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
+
+
+
-

-
-
-
+
+
+
-

-
-
-
-
+
+
+
+
+
+
+
+


-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+



-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+


+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-


 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFIRITests: OTTestCase
{
	OFIRI *_IRI[11];
	OFMutableIRI *_mutableIRI;
}
@end
static OFString *const module = @"OFIRI";
static OFString *IRIString = @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/"

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
@implementation OFIRITests
- (void)setUp
{
	void *pool = objc_autoreleasePoolPush();
	[super setUp];
	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://ä/ö?ü"]))
	_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"];
}
	EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #1",
	    OFInvalidFormatException,
	    [OFIRI IRIWithString: @"ht,tp://foo"])

- (void)dealloc
{
	for (uint_fast8_t i = 0; i < 11; i++)
		[_IRI[i] release];

	[_mutableIRI release];

	EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #2",
	    OFInvalidFormatException,
	    [OFIRI IRIWithString: @"http://f`oo"])

	EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #3",
	    OFInvalidFormatException,
	[super dealloc];
}

- (void)testIRIWithStringFailsWithInvalidCharacters
{
	OTAssertThrowsSpecific([OFIRI IRIWithString: @"ht,tp://foo"],
	    OFInvalidFormatException);
	    [OFIRI IRIWithString: @"http://foo/`"])

	EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #4",
	    OFInvalidFormatException,
	    [OFIRI IRIWithString: @"http://foo/foo?`"])
	OTAssertThrowsSpecific([OFIRI IRIWithString: @"http://f`oo"],
	    OFInvalidFormatException);

	OTAssertThrowsSpecific([OFIRI IRIWithString: @"http://foo/`"],
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #5",
	    OFInvalidFormatException,
	    [OFIRI IRIWithString: @"http://foo/foo?foo#`"])
	OTAssertThrowsSpecific([OFIRI IRIWithString: @"http://foo/foo?`"],
	    OFInvalidFormatException);

	OTAssertThrowsSpecific([OFIRI IRIWithString: @"http://foo/foo?foo#`"],
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #6",
	    OFInvalidFormatException,
	OTAssertThrowsSpecific([OFIRI IRIWithString: @"https://[g]/"],
	    OFInvalidFormatException);
	    [OFIRI IRIWithString: @"https://[g]/"])

	EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #7",
	    OFInvalidFormatException,
	    [OFIRI IRIWithString: @"https://[f]:/"])
	OTAssertThrowsSpecific([OFIRI IRIWithString: @"https://[f]:/"],
	    OFInvalidFormatException);

	OTAssertThrowsSpecific([OFIRI IRIWithString: @"https://[f]:f/"],
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #8",
	    OFInvalidFormatException,
	OTAssertThrowsSpecific([OFIRI IRIWithString: @"foo:"],
	    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(
- (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");
}

	    @"+[IRIWithString:relativeToIRI:] fails with invalid characters #1",
	    OFInvalidFormatException,
	    [OFIRI IRIWithString: @"`" relativeToIRI: IRI1])

- (void)testIRIWithStringRelativeToIRIFailsWithInvalidCharacters
{
	EXPECT_EXCEPTION(
	    @"+[IRIWithString:relativeToIRI:] fails with invalid characters #2",
	    OFInvalidFormatException,
	OTAssertThrowsSpecific(
	    [OFIRI IRIWithString: @"`" relativeToIRI: _IRI[0]],
	    OFInvalidFormatException);
	    [OFIRI IRIWithString: @"/`" relativeToIRI: IRI1])

	EXPECT_EXCEPTION(
	    @"+[IRIWithString:relativeToIRI:] fails with invalid characters #3",
	    OFInvalidFormatException,
	OTAssertThrowsSpecific(
	    [OFIRI IRIWithString: @"/`" relativeToIRI: _IRI[0]],
	    OFInvalidFormatException);
	    [OFIRI IRIWithString: @"?`" relativeToIRI: IRI1])

	EXPECT_EXCEPTION(
	    @"+[IRIWithString:relativeToIRI:] fails with invalid characters #4",
	    OFInvalidFormatException,
	    [OFIRI IRIWithString: @"#`" relativeToIRI: IRI1])
	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"])
- (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");

	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"] &&
	OTAssertEqualObjects(
	    [IRI7.host isEqual: @"12:34::56:abcd"] &&
	    IRI8.host == nil && IRI9.host == nil && IRI10.host == nil &&
	    [IRI11.host isEqual: @"ä"])
	    [[OFIRI IRIWithString: @"http://host/foo//bar/baz/"]
	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: @"/ö"])
	    lastPathComponent],
	    @"baz");

	OTAssertEqualObjects([[OFIRI IRIWithString: @"http://host/foo/"]
	    lastPathComponent],
	    @"foo");

	OTAssertEqualObjects([[OFIRI IRIWithString: @"http://host/"]
	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],
	    @"/");

	    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:
	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
	    [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
	    isEqual: [OFIRI IRIWithString: @"http://%C3%A4/%C3%B6?%C3%BC"]])

{
	mutableIRI = [OFMutableIRI IRIWithScheme: @"dummy"];

	OTAssertEqualObjects(
	EXPECT_EXCEPTION(
	    @"-[setPercentEncodedScheme:] with invalid characters fails",
	    _IRI[10].IRIByAddingPercentEncodingForUnicodeCharacters,
	    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") &&
	    [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.percentEncodedHost isEqual: @"[12:34:aB]"] &&
	    (mutableIRI.host = @"12:34:g") &&
	    [mutableIRI.percentEncodedHost isEqual: @"12%3A34%3Ag"])
	_mutableIRI.host = @"12:34:aB";
	OTAssertEqualObjects(_mutableIRI.percentEncodedHost, @"[12:34:aB]");

	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"])
	_mutableIRI.host = @"12:34:g";
	OTAssertEqualObjects(_mutableIRI.percentEncodedHost, @"12%3A34%3Ag");
}

- (void)testSetPercentEncodedHost
{
	_mutableIRI.percentEncodedHost = @"ho%3Ast";
	OTAssertEqualObjects(_mutableIRI.host, @"ho:st");

	EXPECT_EXCEPTION(
	    @"-[setPercentEncodedHost:] with invalid characters fails #1",
	    OFInvalidFormatException,
	    mutableIRI.percentEncodedHost = @"/")

	EXPECT_EXCEPTION(
	    @"-[setPercentEncodedHost:] with invalid characters fails #2",
	    OFInvalidFormatException,
	_mutableIRI.percentEncodedHost = @"[12:34]";
	OTAssertEqualObjects(_mutableIRI.host, @"12:34");

	_mutableIRI.percentEncodedHost = @"[12::ab]";
	OTAssertEqualObjects(_mutableIRI.host, @"12::ab");
}

- (void)testSetPercentEncodedHostFailsWithInvalidCharacters
{
	OTAssertThrowsSpecific(_mutableIRI.percentEncodedHost = @"/",
	    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,
	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);
	    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,
}

- (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);
	    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,
}

- (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);
	    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,
}

- (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);
	    mutableIRI.percentEncodedQuery = @"`")

	TEST(@"-[setQueryItems:]",
	    (mutableIRI.queryItems = [OFArray arrayWithObjects:
}

- (void)testSetQueryItems
{
	_mutableIRI.queryItems = [OFArray arrayWithObjects:
	    [OFPair pairWithFirstObject: @"foo&bar" secondObject: @"baz=qux"],
	    [OFPair pairWithFirstObject: @"f=oobar" secondObject: @"b&azqux"],
	    nil];
	    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,
	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);
	    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"]])
}

-(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"]);

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFInvocationTests.m from [23f57f7d3a] to [11b71f5c4f].

17
18
19
20
21
22
23
24


25


26



27
28
29
30
31
32
33

34
35
36
37


38
39

40
41
42

43

44

45

46
47
48





























49
50
51
52
53
54
55
56



57
58
59
60

61
62
63
64
65
66
67
68
69

70
71
72

73
74
75

76
77
78

79
80
81


82
83
84


85
86
87


88
89
90

91
92

93
94
95
17
18
19
20
21
22
23

24
25
26
27
28

29
30
31
32
33
34
35
36
37

38
39
40


41
42
43

44
45
46

47
48
49

50
51
52



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86



87
88
89
90



91

92







93



94



95



96
97


98
99
100


101
102
103


104
105
106


107


108

109
110







-
+
+

+
+
-
+
+
+






-
+


-
-
+
+

-
+


-
+

+
-
+

+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
+
+
+

-
-
-
+
-

-
-
-
-
-
-
-
+
-
-
-
+
-
-
-
+
-
-
-
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
-
-
+
-



#include <string.h>

#if defined(HAVE_COMPLEX_H) && !defined(__STDC_NO_COMPLEX__)
# include <complex.h>
#endif

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFInvocationTests: OTTestCase
{
static OFString *const module = @"OFInvocation";
	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 st;
	return testStruct;
}

- (void)invocationTests
- (void)setUp
{
	[super setUp];
	void *pool = objc_autoreleasePoolPush();

	SEL selector = @selector(invocationTestMethod1::::);
	OFMethodSignature *signature =
	OFMethodSignature *sig = [self methodSignatureForSelector: selector];
	OFInvocation *invocation;
	struct TestStruct st, st2, *stp = &st, *stp2;
	    [self methodSignatureForSelector: selector];

	_invocation = [[OFInvocation alloc] initWithMethodSignature: signature];
}

- (void)dealloc
{
	[_invocation release];

	[super dealloc];
}

- (void)testSetAndGetReturnValue
{
	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)testSetAndGetArgumentAtIndex
{
	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;
	memset(&testStruct, 0xFF, sizeof(testStruct));
	testStruct.c = 0x55;
	testStruct.i = 0xAAAAAAAA;

	TEST(@"+[invocationWithMethodSignature:]",
	    (invocation = [OFInvocation invocationWithMethodSignature: sig]))

	memset(&testStruct2, 0, sizeof(testStruct2));
	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]))
	[_invocation setArgument: &c atIndex: 2];

	TEST(@"-[setArgument:atIndex:] #2",
	    R([invocation setArgument: &i atIndex: 3]))
	[_invocation setArgument: &i atIndex: 3];

	TEST(@"-[setArgument:atIndex:] #3",
	    R([invocation setArgument: &stp atIndex: 4]))
	[_invocation setArgument: &testStructPtr atIndex: 4];

	TEST(@"-[setArgument:atIndex:] #4",
	    R([invocation setArgument: &st atIndex: 5]))
	[_invocation setArgument: &testStruct atIndex: 5];

	TEST(@"-[getArgument:atIndex:] #1",
	    R([invocation getArgument: &c2 atIndex: 2]) && c == c2)
	[_invocation getArgument: &c2 atIndex: 2];
	OTAssertEqual(c, c2);

	TEST(@"-[getArgument:atIndex:] #2",
	    R([invocation getArgument: &i2 atIndex: 3]) && i == i2)
	[_invocation getArgument: &i2 atIndex: 3];
	OTAssertEqual(i, i2);

	TEST(@"-[getArgument:atIndex:] #3",
	    R([invocation getArgument: &stp2 atIndex: 4]) && stp == stp2)
	[_invocation getArgument: &testStructPtr2 atIndex: 4];
	OTAssertEqual(testStructPtr, testStructPtr2);

	TEST(@"-[getArgument:atIndex:] #4",
	    R([invocation getArgument: &st2 atIndex: 5]) &&
	[_invocation getArgument: &testStruct2 atIndex: 5];
	    memcmp(&st, &st2, sizeof(st)) == 0)

	OTAssertEqual(memcmp(&testStruct, &testStruct2, sizeof(testStruct)), 0);
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFJSONTests.m from [92f1f602a7] to [2e899d37b6].

11
12
13
14
15
16
17
18


19
20
21
22
23
24






25
26
27
28









29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
























































69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86






87
88
89


90
91
92
11
12
13
14
15
16
17

18
19
20





21
22
23
24
25
26




27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44































45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115



116
117
118
119
120
121
122


123
124

125
126







-
+
+

-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+









-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+















-
-
-
+
+
+
+
+
+

-
-
+
+
-


 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFJSON";

@implementation TestsAppDelegate (JSONTests)
- (void)JSONTests
{
@interface OFJSONTests: OTTestCase
{
	OFDictionary *_dictionary;
}
@end

	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:
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:
	    [OFArray arrayWithObject: [OFArray arrayWithObject:
	    [OFArray arrayWithObject: [OFArray arrayWithObject:
	    [OFArray arrayWithObject: [OFArray arrayWithObject:
	    [OFArray arrayWithObject: [OFArray arrayWithObject:
	    [OFArray arrayWithObject: [OFArray arrayWithObject:
	    [OFArray arrayWithObject: [OFArray arrayWithObject:
	    [OFArray arrayWithObject: [OFArray arrayWithObject:
	    [OFArray arrayWithObject: [OFArray arrayWithObject:
	    [OFArray arrayWithObject: [OFArray arrayWithObject:
	    [OFArray arrayWithObject:
	    [OFDictionary dictionary]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]])

	EXPECT_EXCEPTION(@"-[objectByParsingJSON] #7", OFInvalidJSONException,
	    [OFDictionary dictionary]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]);
}

- (void)testObjectByParsingJSONFailsWithTooDeepNesting
{
	OTAssertThrowsSpecific(
	    [@"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"
	    objectByParsingJSON])

	    objectByParsingJSON],
	    OFInvalidJSONException);
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFKernelEventObserverTests.m from [fd27260531] to [2177e01800].

11
12
13
14
15
16
17



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

33
34
35
36
37

38
39
40
41
42

43
44
45
46
47
48
49
50

51
52
53
54




55
56

57
58

59
60
61
62



63
64
65
66
67




68
69
70
71
72
73
74
75
76
77
78
79


80
81
82
83
84
85

86
87
88

89
90
91



92
93

94
95
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154




155
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
182
183
184


185
186
187

188
189
190
191
192
193
194
195
196
197

198
199
200
201

202
203
204

205
206
207

208
209
210

211
212
213

214
215
216


217
218

219
220

221
222

223

224
225
226
227



228

229
230
231
232



233
234
235

236
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33


34





35
36



37
38
39

40


41
42

43




44
45
46
47


48
49

50
51



52
53
54
55




56
57
58
59






60
61
62
63


64
65
66
67
68
69
70

71
72

73
74
75


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

91











92









93
94
95
96
97
98
99
100

101









102
103
104





105
106



107
108
109
110









111

112





113
114

115











116
117
118
119

120
121
122

123






124




125



126



127



128

129

130



131
132


133


134


135
136
137
138
139
140

141
142
143
144
145
146
147
148

149
150
151
152

153
154
155







+
+
+













-
-
+
-
-
-
-
-
+

-
-
-

+

-

-
-


-
+
-
-
-
-
+
+
+
+
-
-
+

-
+

-
-
-
+
+
+

-
-
-
-
+
+
+
+
-
-
-
-
-
-




-
-
+
+





-
+

-

+

-
-
+
+
+


+









-
+
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-








-
+
-
-
-
-
-
-
-
-
-



-
-
-
-
-


-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
-

-
-
-
-
-


-
+
-
-
-
-
-
-
-
-
-
-
-
+
+


-
+


-

-
-
-
-
-
-
+
-
-
-
-
+
-
-
-
+
-
-
-
+
-
-
-
+
-

-
+
-
-
-
+
+
-
-
+
-
-
+
-
-
+

+



-
+
+
+

+



-
+
+
+

-

+

 * 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 "ObjFW.h"
#import "ObjFWTest.h"

#ifdef HAVE_KQUEUE
# import "OFKqueueKernelEventObserver.h"
#endif
#ifdef HAVE_EPOLL
# import "OFEpollKernelEventObserver.h"
#endif
#ifdef HAVE_POLL
# import "OFPollKernelEventObserver.h"
#endif
#ifdef HAVE_SELECT
# import "OFSelectKernelEventObserver.h"
#endif

#import "TestsAppDelegate.h"

@interface OFKernelEventObserverTests: OTTestCase
static const size_t numExpectedEvents = 3;

static OFString *module;

@interface ObserverTest: OFObject <OFKernelEventObserverDelegate>
    <OFKernelEventObserverDelegate>
{
@public
	TestsAppDelegate *_testsAppDelegate;
	OFKernelEventObserver *_observer;
	OFTCPSocket *_server, *_client, *_accepted;
	OFKernelEventObserver *_observer;
	size_t _events;
	int _fails;
}

- (void)run;
@end

@implementation ObserverTest
static const size_t numExpectedEvents = 3;
- (instancetype)initWithTestsAppDelegate: (TestsAppDelegate *)testsAppDelegate
{
	self = [super init];


@implementation OFKernelEventObserverTests
- (void)setUp
{
	@try {
		OFSocketAddress address;
	OFSocketAddress address;

		_testsAppDelegate = testsAppDelegate;
	[super setUp];

		_server = [[OFTCPSocket alloc] init];
		address = [_server bindToHost: @"127.0.0.1" port: 0];
		[_server listen];
	_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];
	_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;
}

- (void)dealloc
{
	[_server release];
	[_client 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)
	OTAssertFalse(deadlineExceeded);
		[_testsAppDelegate
		    outputSuccess: @"-[observe] not exceeding deadline"
			 inModule: module];
	else {
		[_testsAppDelegate
		    outputFailure: @"-[observe] not exceeding deadline"
			 inModule: module];
		_fails++;
	}

	if (_events == numExpectedEvents)
	OTAssertEqual(_events, numExpectedEvents);
		[_testsAppDelegate
		    outputSuccess: @"-[observe] handling all events"
			 inModule: module];
	else {
		[_testsAppDelegate
		    outputFailure: @"-[observe] handling all events"
			 inModule: module];
		_fails++;
	}
}

- (void)objectIsReadyForReading: (id)object
{
	char buffer;

	switch (_events++) {
	case 0:
		if (object == _server)
		OTAssertEqual(object, _server);
			[_testsAppDelegate
			    outputSuccess: @"-[observe] with listening socket"
				 inModule: module];
		else {
			[_testsAppDelegate
			    outputFailure: @"-[observe] with listening socket"
				 inModule: module];
			_fails++;
		}

		_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')
		OTAssert(object, _accepted);

		OTAssertEqual([object readIntoBuffer: &buffer length: 1], 1);
		OTAssertEqual(buffer, '0');
			[_testsAppDelegate
			    outputSuccess: @"-[observe] with data ready to read"
				 inModule: module];
		else {
			[_testsAppDelegate
			    outputFailure: @"-[observe] with data ready to read"
				 inModule: module];
			_fails++;
		}


		[_client close];

		[_testsAppDelegate
		    outputTesting: @"-[observe] with closed connection"
			 inModule: module];

		break;
	case 2:
		if (object == _accepted &&
		OTAssertEqual(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++;
		}


		OTAssertEqual([object readIntoBuffer: &buffer length: 1], 0);
		break;
	default:
		OFEnsure(0);
		OTAssert(false);
	}
}
@end

@implementation TestsAppDelegate (OFKernelEventObserverTests)
- (void)kernelEventObserverTestsWithClass: (Class)class
{
	void *pool = objc_autoreleasePoolPush();
	ObserverTest *test;

#ifdef HAVE_SELECT
	module = [class className];
	test = [[[ObserverTest alloc]
	    initWithTestsAppDelegate: self] autorelease];

- (void)testSelectKernelEventObserver
	TEST(@"+[observer]", (test->_observer = [class observer]))
	test->_observer.delegate = test;

{
	TEST(@"-[addObjectForReading:]",
	    R([test->_observer addObjectForReading: test->_server]))

	[self testKernelEventObserverWithClass:
	[test run];
	_fails += test->_fails;

	    [OFSelectKernelEventObserver class]];
	objc_autoreleasePoolPop(pool);
}

#endif
- (void)kernelEventObserverTests
{
#ifdef HAVE_SELECT

#ifdef HAVE_POLL
	[self kernelEventObserverTestsWithClass:
	    [OFSelectKernelEventObserver class]];
- (void)testPollKernelEventObserver
#endif

{
#ifdef HAVE_POLL
	[self kernelEventObserverTestsWithClass:
	[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

Modified tests/OFListTests.m from [39ffbcd436] to [48a2f18d5f].

11
12
13
14
15
16
17
18


19





20
21
22
23
24
25
26
27
28
29
30

























31
32
33
34
35
36







37
38


39


40
41
42
43
44
45
46
47
48

















49
50
51
52
53
54
55
56
57
58
59
60
61






























62
63
64
65
66
67
68
69
70
71












72


73



74


75
76
77











78
79
80
81
82







83


84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106















































107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126


































127



128
129
130
131
132
133
134
135
136
137
























138
139
140


141
142

143
144
145

146
147
148
149

150
151
152
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25











26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50






51
52
53
54
55
56
57
58

59
60
61
62
63









64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81












82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112









113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

128
129
130
131
132
133



134
135
136
137
138
139
140
141
142
143
144
145




146
147
148
149
150
151
152
153
154
155























156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203



















204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241










242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266


267
268


269
270
271

272
273
274


275

276
277







-
+
+

+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+

-
+
+

+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
+
+
+

+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+

+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
-
-
+


-
+


-
-
+
-


 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFListTests: OTTestCase
{
	OFList *_list;
}
@end
static OFString *const module = @"OFList";
static OFString *strings[] = {
	@"Foo",
	@"Bar",
	@"Baz"
};

@implementation TestsAppDelegate (OFListTests)
- (void)listTests
{
	void *pool = objc_autoreleasePoolPush();

@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
	OFList *list;
	OFEnumerator *enumerator;
	OFListItem iter;
	OFString *object;
	size_t i;
	bool ok;
{
	OFListItem item;

	[_list appendObject: @"Qux"];

	item = _list.firstListItem;
	OTAssertEqualObjects(OFListItemObject(item), @"Foo");

	TEST(@"+[list]", (list = [OFList list]))
	item = OFListItemNext(item);
	OTAssertEqualObjects(OFListItemObject(item), @"Bar");

	item = OFListItemNext(item);
	OTAssertEqualObjects(OFListItemObject(item), @"Baz");
	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]])

	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");
}

	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])
- (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");

	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]])
	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");
	TEST(@"-[count]", list.count == 3)

	item = OFListItemNext(item);
	OTAssertEqualObjects(OFListItemObject(item), @"Bar");

	item = OFListItemNext(item);
	OTAssertEqualObjects(OFListItemObject(item), @"Qux");
	TEST(@"-[containsObject:]",
	    [list containsObject: strings[1]] &&
	    ![list containsObject: @"nonexistent"])

	item = OFListItemNext(item);
	OTAssertEqualObjects(OFListItemObject(item), @"Baz");

	item = OFListItemNext(item);
	OTAssertEqual(item, NULL);
}

- (void)testInsertObjectAfterListItem
{
	OFListItem item;

	TEST(@"-[containsObjectIdenticalTo:]",
	    [list containsObjectIdenticalTo: strings[1]] &&
	    ![list containsObjectIdenticalTo:
	    [OFString stringWithString: strings[1]]])
	[_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");
	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++;
	}

	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);

	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;
	[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;
		iter = OFListItemNext(iter);
		i++;
	}

	if (list.count != i)
		ok = false;

	TEST(@"Fast Enumeration", ok)

	ok = false;

	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_;
		for (OFString *object in _list) {
			(void)object;

			[list removeListItem: list.lastListItem];
			[_list removeListItem: _list.firstListItem];
		}
	} @catch (OFEnumerationMutationException *e) {
		ok = true;
		detected = true;
	}

	TEST(@"Detection of mutation during Fast Enumeration", ok)

	OTAssertTrue(detected);
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFLocaleTests.m from [bb15c236d6] to [c000e04b91].

11
12
13
14
15
16
17
18


19
20
21
22



23
24



25

26
27
28



29
30
31


32
33
34


35
36
37

38
39

40
41
11
12
13
14
15
16
17

18
19
20



21
22
23


24
25
26

27
28


29
30
31
32


33
34



35
36
37


38
39

40
41
42







-
+
+

-
-
-
+
+
+
-
-
+
+
+
-
+

-
-
+
+
+

-
-
+
+
-
-
-
+
+

-
-
+

-
+


 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@implementation TestsAppDelegate (OFLocaleTests)
- (void)localeTests
{
@interface OFLocaleTests: OTTestCase
@end

	void *pool = objc_autoreleasePoolPush();

@implementation OFLocaleTests
+ (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, id) *) *)summary
{
	[OFStdOut setForegroundColor: [OFColor lime]];
	OFMutableArray *summary = [OFMutableArray array];

	[OFStdOut writeFormat: @"[OFLocale] Language code: %@\n",
	    [OFLocale languageCode]];
#define ADD(name, value)						\
	[summary addObject: [OFPair pairWithFirstObject: name		\
					   secondObject: value]];

	[OFStdOut writeFormat: @"[OFLocale] Country code: %@\n",
	    [OFLocale countryCode]];
	ADD(@"Language code", [OFLocale languageCode])
	ADD(@"Country code", [OFLocale countryCode])

	[OFStdOut writeFormat: @"[OFLocale] Encoding: %@\n",
	    OFStringEncodingName([OFLocale encoding])];
	ADD(@"Encoding", OFStringEncodingName([OFLocale encoding]))
	ADD(@"Decimal separator", [OFLocale decimalSeparator])

	[OFStdOut writeFormat: @"[OFLocale] Decimal separator: %@\n",
	    [OFLocale decimalSeparator]];
#undef ADD

	objc_autoreleasePoolPop(pool);
	return summary;
}
@end

Deleted tests/OFMD5HashTests.m version [96fc59cb59].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59



























































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <string.h>

#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

Modified tests/OFMatrix4x4Tests.m from [29d0338fbf] to [25395304c9].

11
12
13
14
15
16
17
18
19


20
21
22
23

24
25
26

27
28
29
30
31




32
33
34
35
36
37
38
39





40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
















































56
57
58
59


60
61
62
63
64
65
66
67













68
69

70
71
72
73
74
75
76
77
78



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104














































105
106
11
12
13
14
15
16
17


18
19

20


21
22


23





24
25
26
27








28
29
30
31
32
33
34
35
36












37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90








91
92
93
94
95
96
97
98
99
100
101
102
103
104

105
106
107
108
109
110




111
112
113
114
115
116
117






















118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165







-
-
+
+
-

-
-
+

-
-
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+




-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+





-
-
-
-
+
+
+




-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


 * 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 "TestsAppDelegate.h"

#import "ObjFW.h"
#import "ObjFWTest.h"
static OFString *const module = @"OFMatrix4x4Tests";

@implementation TestsAppDelegate (OFMatrix4x4Tests)
- (void)matrix4x4Tests
@interface OFMatrix4x4Tests: OTTestCase
{
	void *pool = objc_autoreleasePoolPush();
	OFMatrix4x4 *matrix, *matrix2;
	OFMatrix4x4 *_matrix;
	OFVector4D point;
	OFVector4D points[2] = {{ 1, 2, 3, 1 }, { 7, 8, 9, 2 }};

	TEST(@"+[identityMatrix]",
	    memcmp([[OFMatrix4x4 identityMatrix] values], (const float [4][4]){
}
@end

@implementation OFMatrix4x4Tests
		{ 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]){
- (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: @"<OFMatrix4x4: {\n"
					 @"\t1 2 3 4\n"
					 @"\t5 6 7 8\n"
					 @"\t9 10 11 12\n"
					 @"\t13 14 15 16\n"
					 @"}>"])

	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,
	    @"<OFMatrix4x4: {\n"
	    @"\t1 2 3 4\n"
	    @"\t5 6 7 8\n"
	    @"\t9 10 11 12\n"
	    @"\t13 14 15 16\n"
	    @"}>");
}

- (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 }
	    }]) hash]);
}
	    }]])

	TEST(@"-[copy]", (matrix2 = [[matrix copy] autorelease]) &&
	    [matrix2 isEqual: matrix])

	TEST(@"-[multiplyWithMatrix:] #1",
	    R([matrix2 multiplyWithMatrix: [OFMatrix4x4 identityMatrix]]) &&
	    [matrix2 isEqual: matrix])

- (void)testCopy
{
	OTAssertEqualObjects([[_matrix copy] autorelease], _matrix);
}

- (void)testMultiplyWithMatrix
{
	OFMatrix4x4 *matrix;

	matrix = [[_matrix copy] autorelease];
	[matrix multiplyWithMatrix: [OFMatrix4x4 identityMatrix]];
	OTAssertEqualObjects(matrix, _matrix);

	matrix2 = [OFMatrix4x4 matrixWithValues: (const float [4][4]){
	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

Modified tests/OFMemoryStreamTests.m from [8479ce3790] to [be569a5484].

11
12
13
14
15
16
17
18


19


20

21
22
23
24


25
26
27

28
29
30
31
32
33
34




35
36
37
38
39
40
41
42
43
44
45
46
47






48
49

50
51
52
53
54
55
56
57
58
59
60
61













62
63
64
65
66









67
68
69
70
71
72
73
74
75
76






77
78
79
80
81


82
83
84

85
86
87
11
12
13
14
15
16
17

18
19
20
21
22

23
24
25


26
27
28


29







30
31
32
33
34
35
36
37
38
39







40
41
42
43
44
45


46
47











48
49
50
51
52
53
54
55
56
57
58
59
60
61




62
63
64
65
66
67
68
69
70
71
72
73
74






75
76
77
78
79
80
81




82
83



84


85







-
+
+

+
+
-
+


-
-
+
+

-
-
+
-
-
-
-
-
-
-
+
+
+
+






-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+




-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
+
+
-
-
-
+
-
-

 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFMemoryStreamTests: OTTestCase
@end
static OFString *const module = @"OFMemoryStream";

static const char string[] = "abcdefghijkl";

@implementation TestsAppDelegate (OFMemoryStreamTests)
- (void)memoryStreamTests
@implementation OFMemoryStreamTests
- (void)testReadOnlyMemoryStream
{
	void *pool = objc_autoreleasePoolPush();
	OFMemoryStream *stream;
	OFMemoryStream *stream = [OFMemoryStream
	char buffer[10];
	OFMutableData *data;

	TEST(@"+[streamWithMemoryAddress:size:writable:]",
	    (stream = [OFMemoryStream streamWithMemoryAddress: (char *)string
							 size: sizeof(string)
						     writable: false]));
	    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)
	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);

	TEST(@"-[lowlevelIsAtEndOfStream]", [stream lowlevelIsAtEndOfStream])
	OTAssertTrue([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)
	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);

	EXPECT_EXCEPTION(@"Writes rejected on read-only stream",
	    OFWriteFailedException, [stream lowlevelWriteBuffer: "" length: 1])

	data = [OFMutableData dataWithCapacity: 13];
	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)

	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);

	EXPECT_EXCEPTION(@"Out of bound writes rejected",
	    OFWriteFailedException,
	    [stream lowlevelWriteBuffer: "xyz" length: 4])

	OTAssertThrowsSpecific([stream lowlevelWriteBuffer: "xyz" length: 4],
	    OFWriteFailedException);
	TEST(@"Partial write for too long write",
	    memcmp(data.items, "abcdefghijxyz", 13) == 0)

}
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFMethodSignatureTests.m from [f874f8bb5c] to [27f044c8f7].

17
18
19
20
21
22
23
24


25
26


27
28
29
30
31
32
33
17
18
19
20
21
22
23

24
25
26

27
28
29
30
31
32
33
34
35







-
+
+

-
+
+








#include <string.h>

#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H)
# include <complex.h>
#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;
};

65
66
67
68
69
70
71
72
73


74
75
76
77
78
79


80
81
82
83
84
85
86
87
88
89
90
91











92
93
94

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109














110
111
112
113
114





115
116
117
118


119
120
121
122
123
124
125
126
127












128
129
130
131


132
133
134
135
136
137


138
139
140
141
142


143
144
145
146


147
148
149
150
151
152
153
154








155
156
157
158


159
160
161
162
163
164


165
166
167
168
169


170
171
172
173


174
175
176
177



178
179
180
181
67
68
69
70
71
72
73


74
75
76

77
78


79
80












81
82
83
84
85
86
87
88
89
90
91
92


93
94














95
96
97
98
99
100
101
102
103
104
105
106
107
108





109
110
111
112
113

114


115
116

117







118
119
120
121
122
123
124
125
126
127
128
129
130



131
132
133
134
135



136
137
138
139



140
141
142



143
144
145







146
147
148
149
150
151
152
153
154



155
156
157
158
159



160
161
162
163



164
165
166



167
168
169



170
171
172


173
174







-
-
+
+

-


-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-

-
-
+
+
-

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+



-
-
-
+
+


-
-
-
+
+

-
-
-
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
+
+



-
-
-
+
+


-
-
-
+
+

-
-
-
+
+

-
-
-
+
+
+
-
-


	int i;
	union {
		float f;
		double d;
	} u;
};

@implementation TestsAppDelegate (OFMethodSignatureTests)
- (void)methodSignatureTests
@implementation OFMethodSignatureTests
- (void)testSignatureWithObjCTypes
{
	void *pool = objc_autoreleasePoolPush();
	OFMethodSignature *methodSignature;

	TEST(@"-[signatureWithObjCTypes:] #1",
	    (methodSignature = [OFMethodSignature signatureWithObjCTypes:
	methodSignature =
	    [OFMethodSignature signatureWithObjCTypes: "i28@0:8S16*20"];
	    "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)
	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);

	TEST(@"-[signatureWithObjCTypes:] #2",
	    (methodSignature = [OFMethodSignature signatureWithObjCTypes:
	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",
	    "^{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);
}

	    OFInvalidFormatException,
	    [OFMethodSignature signatureWithObjCTypes: "{ii"])

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #4",
	    OFInvalidFormatException,
- (void)testSignatureWithObjCTypesFailsWithInvalidFormat
{
	OTAssertThrowsSpecific(
	    [OFMethodSignature signatureWithObjCTypes: "{ii"],
	    OFInvalidFormatException);
	    [OFMethodSignature signatureWithObjCTypes: ""])

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #5",
	    OFInvalidFormatException,
	OTAssertThrowsSpecific([OFMethodSignature signatureWithObjCTypes: ""],
	    OFInvalidFormatException);
	    [OFMethodSignature signatureWithObjCTypes: "0"])

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #6",
	    OFInvalidFormatException,
	    [OFMethodSignature signatureWithObjCTypes: "{{}0"])

	TEST(@"OFSizeOfTypeEncoding() #1",
	    OFSizeOfTypeEncoding(@encode(struct Test1Struct)) ==
	    sizeof(struct Test1Struct))
	OTAssertThrowsSpecific([OFMethodSignature signatureWithObjCTypes: "0"],
	    OFInvalidFormatException);

	OTAssertThrowsSpecific(
	    [OFMethodSignature signatureWithObjCTypes: "{{}0"],
	    OFInvalidFormatException);
}

- (void)testSizeOfTypeEncoding
{
	OTAssertEqual(OFSizeOfTypeEncoding(@encode(struct Test1Struct)),
	    sizeof(struct Test1Struct));

	TEST(@"OFSizeOfTypeEncoding() #2",
	    OFSizeOfTypeEncoding(@encode(struct Test2Struct)) ==
	    sizeof(struct Test2Struct))
	OTAssertEqual(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))
	OTAssertEqual(OFSizeOfTypeEncoding(@encode(struct Test3Struct)),
	    sizeof(struct Test3Struct));
#endif

	TEST(@"OFSizeOfTypeEncoding() #4",
	    OFSizeOfTypeEncoding(@encode(union Test3Union)) ==
	    sizeof(union Test3Union))
	OTAssertEqual(OFSizeOfTypeEncoding(@encode(union Test3Union)),
	    sizeof(union Test3Union));

	TEST(@"OFSizeOfTypeEncoding() #5",
	    OFSizeOfTypeEncoding(@encode(union Test4Union)) ==
	    sizeof(union Test4Union))
	OTAssertEqual(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))
	OTAssertEqual(OFSizeOfTypeEncoding(@encode(struct Test1Struct [5])),
	    sizeof(struct Test1Struct [5]));
}

- (void)testAlignmentOfTypeEncoding
{
	OTAssertEqual(OFAlignmentOfTypeEncoding(@encode(struct Test1Struct)),
	    OF_ALIGNOF(struct Test1Struct));

	TEST(@"OFAlignmentOfTypeEncoding() #2",
	    OFAlignmentOfTypeEncoding(@encode(struct Test2Struct)) ==
	    OF_ALIGNOF(struct Test2Struct))
	OTAssertEqual(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))
	OTAssertEqual(OFAlignmentOfTypeEncoding(@encode(struct Test3Struct)),
	    OF_ALIGNOF(struct Test3Struct));
#endif

	TEST(@"OFAlignmentOfTypeEncoding() #4",
	    OFAlignmentOfTypeEncoding(@encode(union Test3Union)) ==
	    OF_ALIGNOF(union Test3Union))
	OTAssertEqual(OFAlignmentOfTypeEncoding(@encode(union Test3Union)),
	    OF_ALIGNOF(union Test3Union));

	TEST(@"OFAlignmentOfTypeEncoding() #5",
	    OFAlignmentOfTypeEncoding(@encode(union Test4Union)) ==
	    OF_ALIGNOF(union Test4Union))
	OTAssertEqual(OFAlignmentOfTypeEncoding(@encode(union Test4Union)),
	    OF_ALIGNOF(union Test4Union));

	TEST(@"OFAlignmentOfTypeEncoding() #6",
	    OFAlignmentOfTypeEncoding(@encode(struct Test1Struct [5])) ==
	    OF_ALIGNOF(struct Test1Struct [5]))
	OTAssertEqual(
	    OFAlignmentOfTypeEncoding(@encode(struct Test1Struct [5])),
	    OF_ALIGNOF(struct Test1Struct [5]));

	objc_autoreleasePoolPop(pool);
}
@end

Added tests/OFMutableArrayTests.h version [5ae96e61d9].























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [29c5b449b7].
































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [6267c8f315].






















































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <string.h>

#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 version [426f7697f8].


























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [4f6485a158].















































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [a531cff94b].























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [8420adbe6c].















































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [206d9c71b6].


























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 version [9349febbaa].

























































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <string.h>

#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 version [be500c9e1a].































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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

Modified tests/OFNotificationCenterTests.m from [088812ee19] to [0385b37634].

11
12
13
14
15
16
17
18


19
20
21
22
23
24
25
26




27
28
29
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47
48


49
50
51
52

53
54
55
56

57
58
59

60
61

62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87




















88
89
90
91
92
93
94





95
96
97
98
99
100
101





102
103
104
105
106
107
108





109
110
111
112
113
114
115
116
117
118
119
120





121

122
123
124






125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
















149
150
151
152
153
154
155
156





157
158
159
11
12
13
14
15
16
17

18
19
20

21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49


50
51
52

53

54
55
56


57
58


59


60
61


62
63
64





















65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87




88
89
90
91
92
93
94
95




96
97
98
99
100
101
102
103




104
105
106
107
108
109
110
111
112
113
114
115





116
117
118
119
120
121
122



123
124
125
126
127
128
129
130
131
132
133
134
135

















136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154





155
156
157
158
159

160
161







-
+
+

-





-
+
+
+
+









-
+










-
-
+
+

-

-
+


-
-
+

-
-
+
-
-
+

-
-
+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
-
-
+
+
+
+
+



-
-
-
-
+
+
+
+
+



-
-
-
-
+
+
+
+
+







-
-
-
-
-
+
+
+
+
+

+
-
-
-
+
+
+
+
+
+







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
-
-
-
+
+
+
+
+
-


 * 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 "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);

	_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];
	test2 = [[[OFNotificationCenterTestClass alloc] init] autorelease];
	test3 =
	    [[[OFNotificationCenterTest 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)

	[center postNotification: notification];
	OTAssertEqual(test1->_received, 1);
	OTAssertEqual(test2->_received, 3);
	OTAssertEqual(test3->_received, 0);
	OTAssertEqual(test4->_received, 0);
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFNumberTests.m from [8e6e4b486d] to [79e5b7d8e7].

11
12
13
14
15
16
17
18


19


20
21
22
23






24

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53



















































54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69




















70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86






















87
88
89


90


91
92
11
12
13
14
15
16
17

18
19
20
21
22




23
24
25
26
27
28
29
30





























31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81


82














83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

















103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124



125
126

127
128
129
130







-
+
+

+
+
-
-
-
-
+
+
+
+
+
+

+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
-
+
+


 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFNumberTests: OTTestCase
{
static OFString *const module = @"OFNumber";

@implementation TestsAppDelegate (OFNumberTests)
- (void)numberTests
	OFNumber *_number;
}
@end

@implementation OFNumberTests
- (void)setUp
{
	[super setUp];
	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 = [[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);
	    number.intValue == INT_MIN &&
	    (number = [OFNumber numberWithInt: INT_MAX]) &&
	OTAssertEqual([[OFNumber numberWithInt: INT_MAX] intValue], 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)

}

- (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
{
	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",
	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);
}

	    (number = [OFNumber numberWithUnsignedLongLong: ULLONG_MAX]) &&
	    number.unsignedLongLongValue == ULLONG_MAX)

- (void)testUnsignedLongLongMaxUnmodified
{
	objc_autoreleasePoolPop(pool);
	OTAssertEqual([[OFNumber numberWithUnsignedLongLong: ULLONG_MAX]
	    unsignedLongLongValue], ULLONG_MAX);
}
@end

Modified tests/OFObjectTests.m from [39c729edc8] to [289bb9d695].

11
12
13
14
15
16
17
18
19


20
21
22
23
24
25
26
27
28
29
30
31
32
33
11
12
13
14
15
16
17


18
19







20
21
22
23
24
25
26







-
-
+
+
-
-
-
-
-
-
-







 * 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 "TestsAppDelegate.h"

#import "ObjFW.h"
#import "ObjFWTest.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";

@interface MyObject: OFObject
{
	id _objectValue;
	Class _classValue;
	bool _boolValue;
	char _charValue;
56
57
58
59
60
61
62


























































































































































































63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261


















































































































































+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




















-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@property (nonatomic) unsigned short unsignedShortValue;
@property (nonatomic) unsigned int unsignedIntValue;
@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, @"<OFObject>");
	OTAssertEqualObjects(_myObject.description, @"<MyObject>");
}

- (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;
@synthesize longValue = _longValue, longLongValue = _longLongValue;
@synthesize unsignedCharValue = _unsignedCharValue;
@synthesize unsignedShortValue = _unsignedShortValue;
@synthesize unsignedIntValue = _unsignedIntValue;
@synthesize unsignedLongValue = _unsignedLongValue;
@synthesize unsignedLongLongValue = _unsignedLongLongValue;
@synthesize floatValue = _floatValue, doubleValue = _doubleValue;

- (void)dealloc
{
	[_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: @"<OFObject>"] &&
	    [myObject.description isEqual: @"<MyObject>"])

	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

Modified tests/OFPBKDF2Tests.m from [2febb34313] to [cd8293bb12].

13
14
15
16
17
18
19
20


21
22
23
24
25








26

27
28
29
30
31
32













33



34
35
36



37
38
39
40
41
42
43
44


45
46
47
48
49
50










51
52
53
54
55
56
57
58


59
60
61
62
63
64










65
66
67
68
69
70
71
72


73
74
75
76




77




78
79
80



81
82
83
84
85
86
87
88


89
90



91
92



93
94
95



96
97
98

99
100
101
102
103
104
105
106
107
108
109
110
111












112
113
114
115
116
117
118
119


120
121
122



123
124
125
13
14
15
16
17
18
19

20
21
22




23
24
25
26
27
28
29
30
31
32






33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49



50
51
52
53
54
55
56
57
58
59
60
61
62






63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82






83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102




103
104
105
106
107
108
109
110
111



112
113
114
115
116
117
118
119
120
121
122
123
124


125
126
127
128
129
130
131
132



133
134
135
136
137

138
139
140
141
142
143
144







145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166



167
168
169


170







-
+
+

-
-
-
-
+
+
+
+
+
+
+
+

+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
-
-
-
+
+
+








+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+








+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+








+
+
-
-
-
-
+
+
+
+

+
+
+
+
-
-
-
+
+
+








+
+
-
-
+
+
+


+
+
+
-
-
-
+
+
+


-
+






-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+








+
+
-
-
-
+
+
+
-
-

 * file.
 */

#include "config.h"

#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFPBKDF2";

@implementation TestsAppDelegate (OFPBKDF2Tests)
- (void)PBKDF2Tests
@interface OFPBKDF2Tests: OTTestCase
{
	OFHMAC *_HMAC;
}
@end

@implementation OFPBKDF2Tests
- (void)setUp
{
	[super setUp];
	void *pool = objc_autoreleasePoolPush();
	OFHMAC *HMAC = [OFHMAC HMACWithHashClass: [OFSHA1Hash class]
			   allowsSwappableMemory: true];
	unsigned char key[25];

	/* Test vectors from RFC 6070 */

	_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(@"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)

	TEST(@"PBKDF2-SHA1, 2 iterations",
	    R(OFPBKDF2((OFPBKDF2Parameters){
		.HMAC                  = HMAC,
	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];

	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)

	TEST(@"PBKDF2-SHA1, 4096 iterations",
	    R(OFPBKDF2((OFPBKDF2Parameters){
		.HMAC                  = HMAC,
	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];

	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
/* This test takes too long, even on a fast machine. */
- (void)testRFC6070TestVector4
{
	unsigned char key[25];
	TEST(@"PBKDF2-SHA1, 16777216 iterations",
	    R(OFPBKDF2((OFPBKDF2Parameters){
		.HMAC                  = HMAC,

	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

- (void)testRFC6070TestVector5
{
	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 *)"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)

	TEST(@"PBKDF2-SHA1, 4096 iterations, key < 1 block",
	    R(OFPBKDF2((OFPBKDF2Parameters){
		.HMAC                  = HMAC,
	});

	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];

	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)

	OTAssertEqual(memcmp(key, "\x56\xFA\x6A\xA7\x55\x48\x09\x9D\xCC\x37\xD7"
	    "\xF0\x34\x25\xE0\xC3", 16), 0);
}
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFPluginTests.m from [278848818e] to [b4655165af].

11
12
13
14
15
16
17
18


19
20
21
22
23

24
25
26

27
28
29
30
31


32
33

34
35
36
37
38

39





40
41


42
43
44
45


46
47
48

49

50
51
52
53

54
55
56
11
12
13
14
15
16
17

18
19
20
21
22


23



24
25




26
27
28

29
30
31
32

33
34

35
36
37
38
39
40

41
42
43



44
45
46

47
48

49
50
51
52

53


54







-
+
+



-
-
+
-
-
-
+

-
-
-
-
+
+

-
+



-

+
-
+
+
+
+
+

-
+
+

-
-
-
+
+

-

+
-
+



-
+
-
-

 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

#import "plugin/TestPlugin.h"

#ifndef OF_IOS
static OFString *const pluginName = @"plugin/TestPlugin";
@interface OFPluginTests: OTTestCase
#else
static OFString *const pluginName = @"PlugIns/TestPlugin";
#endif
@end

static OFString *const module = @"OFPlugin";

@implementation TestsAppDelegate (OFPluginTests)
- (void)pluginTests
@implementation OFPluginTests
- (void)testPlugin
{
	void *pool = objc_autoreleasePoolPush();
	TestPlugin *test = nil;
	OFString *path;
	OFPlugin *plugin;
	Class (*class)(void);
	TestPlugin *test;

#ifndef OF_IOS
	TEST(@"+[pathForName:]", (path = [OFPlugin pathForName: pluginName]))
	path = [OFPlugin pathForName: @"plugin/TestPlugin"];
#else
	path = [OFPlugin pathForName: @"PlugIns/TestPlugin"];
#endif
	OTAssertNotNil(path);

	TEST(@"+[pluginWithPath:]", (plugin = [OFPlugin pluginWithPath: path]))
	plugin = [OFPlugin pluginWithPath: path];
	OTAssertNotNil(plugin);

	TEST(@"-[addressForSymbol:]",
	    (class = (Class (*)(void))(uintptr_t)
	    [plugin addressForSymbol: @"class"]))
	class = (Class (*)(void))(uintptr_t)[plugin addressForSymbol: @"class"];
	OTAssert(class != NULL);

	test = [[class() alloc] init];
	@try {
		test = [[class() alloc] init];
		TEST(@"TestPlugin's -[test:]", [test test: 1234] == 2468)
		OTAssertEqual([test test: 1234], 2468);
	} @finally {
		[test release];
	}

}
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFPropertyListTests.m from [f48a04b761] to [2c2f0ce2a0].

11
12
13
14
15
16
17
18





19
20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76




























77
78

79
80
81
82
83
84
85
86
87
88
89













90
91

92
93

94
95
96



97
98
99
100
101



102
103
104
105
106





107
108


109
110
111
112

113
114
115


116
117
118
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31

32



























33


34

35
36
37
38
39
40
41
42
43
44
45







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73


74
75
76









77
78
79
80
81
82
83
84
85
86
87
88
89


90


91



92
93
94

95



96
97
98

99



100
101
102
103
104
105

106
107
108



109
110


111
112

113
114







-
+
+
+
+
+









-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-

-










+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+


-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
-
-
-
+
+
+
-

-
-
-
+
+
+
-

-
-
-
+
+
+
+
+

-
+
+

-
-
-
+

-
-
+
+
-


 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFPropertyListTests: OTTestCase
@end

#define PLIST(x)							\
	@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"			\
	@"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "	\
	@"\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"		\
	@"<plist version=\"1.0\">\n"					\
	x @"\n"								\
	@"</plist>"

static OFString *const module = @"OFPropertyList";
@implementation OFPropertyListTests
static OFString *const PLIST1 = PLIST(@"<string>Hello</string>");
static OFString *const PLIST2 = PLIST(
    @"<array>"
    @" <string>Hello</string>"
    @" <data>V29ybGQh</data>"
    @" <date>2018-03-14T12:34:56Z</date>"
    @" <true/>"
    @" <false/>"
    @" <real>12.25</real>"
    @" <integer>-10</integer>"
    @"</array>");
static OFString *const PLIST3 = PLIST(
    @"<dict>"
    @" <key>array</key>"
    @" <array>"
    @"  <string>Hello</string>"
    @"  <data>V29ybGQh</data>"
    @"  <date>2018-03-14T12:34:56Z</date>"
    @"  <true/>"
    @"  <false/>"
    @"  <real>12.25</real>"
    @"  <integer>-10</integer>"
    @" </array>"
    @" <key>foo</key>"
    @" <string>bar</string>"
    @"</dict>");

- (void)testObjectByParsingPropertyList
@implementation TestsAppDelegate (OFPLISTParser)
- (void)propertyListTests
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *array = [OFArray arrayWithObjects:
	    @"Hello",
	    [OFData dataWithItems: "World!" count: 6],
	    [OFDate dateWithTimeIntervalSince1970: 1521030896],
	    [OFNumber numberWithBool: true],
	    [OFNumber numberWithBool: false],
	    [OFNumber numberWithFloat: 12.25f],
	    [OFNumber numberWithInt: -10],
	    nil];

	OTAssertEqualObjects([PLIST(
	TEST(@"-[objectByParsingPropertyList:] #1",
	    [PLIST1.objectByParsingPropertyList isEqual: @"Hello"])

	TEST(@"-[objectByParsingPropertyList:] #2",
	    [PLIST2.objectByParsingPropertyList isEqual: array])

	TEST(@"-[objectByParsingPropertyList:] #3",
	    @"<string>Hello</string>") objectByParsingPropertyList],
	    @"Hello");
	OTAssertEqualObjects([PLIST(
	    @"<array>"
	    @" <string>Hello</string>"
	    @" <data>V29ybGQh</data>"
	    @" <date>2018-03-14T12:34:56Z</date>"
	    @" <true/>"
	    @" <false/>"
	    @" <real>12.25</real>"
	    @" <integer>-10</integer>"
	    @"</array>") objectByParsingPropertyList],
	    array);
	OTAssertEqualObjects([PLIST(
	    @"<dict>"
	    @" <key>array</key>"
	    @" <array>"
	    @"  <string>Hello</string>"
	    @"  <data>V29ybGQh</data>"
	    @"  <date>2018-03-14T12:34:56Z</date>"
	    @"  <true/>"
	    @"  <false/>"
	    @"  <real>12.25</real>"
	    @"  <integer>-10</integer>"
	    @" </array>"
	    @" <key>foo</key>"
	    @" <string>bar</string>"
	    @"</dict>") objectByParsingPropertyList],
	    [PLIST3.objectByParsingPropertyList isEqual:
	    [OFDictionary dictionaryWithKeysAndObjects:
	    ([OFDictionary dictionaryWithKeysAndObjects:
	    @"array", array,
	    @"foo", @"bar",
	    nil]])

	EXPECT_EXCEPTION(@"Detecting unsupported version",
	    OFUnsupportedVersionException,
	    [[PLIST(@"<string/>") stringByReplacingOccurrencesOfString: @"1.0"
							    withString: @"1.1"]
	    objectByParsingPropertyList])

	EXPECT_EXCEPTION(
	    nil]));
}

- (void)testDetectUnsupportedVersion
{
	OTAssertThrowsSpecific(
	    [[PLIST(@"<string/>")
	    stringByReplacingOccurrencesOfString: @"1.0"
				      withString: @"1.1"]
	    objectByParsingPropertyList],
	    OFUnsupportedVersionException);
}

	    @"-[objectByParsingPropertyList] detecting invalid format #1",
	    OFInvalidFormatException,
- (void)testDetectInvalidFormat
	    [PLIST(@"<string x='b'/>") objectByParsingPropertyList])

{
	EXPECT_EXCEPTION(
	    @"-[objectByParsingPropertyList] detecting invalid format #2",
	    OFInvalidFormatException,
	OTAssertThrowsSpecific(
	    [PLIST(@"<string x='b'/>") objectByParsingPropertyList],
	    OFInvalidFormatException);
	    [PLIST(@"<string xmlns='foo'/>") objectByParsingPropertyList])

	EXPECT_EXCEPTION(
	    @"-[objectByParsingPropertyList] detecting invalid format #3",
	    OFInvalidFormatException,
	OTAssertThrowsSpecific(
	    [PLIST(@"<string xmlns='foo'/>") objectByParsingPropertyList],
	    OFInvalidFormatException);
	    [PLIST(@"<dict count='0'/>") objectByParsingPropertyList])

	EXPECT_EXCEPTION(
	    @"-[objectByParsingPropertyList] detecting invalid format #4",
	    OFInvalidFormatException,
	OTAssertThrowsSpecific(
	    [PLIST(@"<dict count='0'/>") objectByParsingPropertyList],
	    OFInvalidFormatException);

	OTAssertThrowsSpecific(
	    [PLIST(@"<dict><key/><string/><key/></dict>")
	    objectByParsingPropertyList])
	    objectByParsingPropertyList],
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(
	    @"-[objectByParsingPropertyList] detecting invalid format #5",
	    OFInvalidFormatException,
	OTAssertThrowsSpecific(
	    [PLIST(@"<dict><key x='x'/><string/></dict>")
	    objectByParsingPropertyList])

	    objectByParsingPropertyList],
	    OFInvalidFormatException);
	objc_autoreleasePoolPop(pool);
}
@end

Deleted tests/OFRIPEMD160HashTests.m version [4768731328].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61





























































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <string.h>

#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 version [7856801f0e].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60




























































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <string.h>

#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 version [74da8c08a3].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61





























































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <string.h>

#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 version [1004803a7d].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61





























































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <string.h>

#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 version [7039b1bf58].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62






























































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <string.h>

#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 version [11c494533c].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <string.h>

#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

Modified tests/OFSPXSocketTests.m from [262f8617dc] to [7025b286e5].

12
13
14
15
16
17
18

19
20


21


22




23
24
25
26
27
28
29
30
31
32
33





























































































































































34
35
36
37
38
39
40
12
13
14
15
16
17
18
19
20

21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204







+

-
+
+

+
+
-
+
+
+
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <errno.h>
#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFSPXSocketTests: OTTestCase
{
static OFString *const module = @"OFSPXSocket";
	OFSPXSocket *_sockServer;
	OFSocketAddress _addrServer;
}
@end

@interface SPXSocketDelegate: OFObject <OFSPXSocketDelegate>
{
@public
	OFSequencedPacketSocket *_expectedServerSocket;
	OFSPXSocket *_expectedClientSocket;
	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
  didAcceptSocket: (OFSequencedPacketSocket *)accepted
	exception: (id)exception
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
226
227
228
229
230
231
232
233



























































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
	    network == _expectedNetwork &&
	    memcmp(node, _expectedNode, IPX_NODE_LEN) == 0 &&
	    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

Modified tests/OFSPXStreamSocketTests.m from [37e7d0cfd1] to [096521a07d].

12
13
14
15
16
17
18

19
20


21


22




23
24
25
26
27
28
29
30
31
32
33
































































































































































34
35
36
37
38
39
40
12
13
14
15
16
17
18
19
20

21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207







+

-
+
+

+
+
-
+
+
+
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <errno.h>
#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFSPXStreamSocketTests: OTTestCase
{
static OFString *const module = @"OFSPXStreamSocket";
	OFSPXStreamSocket *_sockServer;
	OFSocketAddress _addrServer;
}
@end

@interface SPXStreamSocketDelegate: OFObject <OFSPXStreamSocketDelegate>
{
@public
	OFStreamSocket *_expectedServerSocket;
	OFSPXStreamSocket *_expectedClientSocket;
	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
  didAcceptSocket: (OFStreamSocket *)accepted
	exception: (id)exception
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
229
230
231
232
233
234
235
236































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
	    network == _expectedNetwork &&
	    memcmp(node, _expectedNode, IPX_NODE_LEN) == 0 &&
	    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

Modified tests/OFScryptTests.m from [3717bd0eab] to [88a05d2135].

13
14
15
16
17
18
19
20


21


22

23
24
25
26
27
28
29
13
14
15
16
17
18
19

20
21
22
23
24

25
26
27
28
29
30
31
32







-
+
+

+
+
-
+







 * file.
 */

#include "config.h"

#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

@interface OFScryptTests: OTTestCase
@end
static OFString *const module = @"OFScrypt";

/* 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,
	0xDF, 0x9B, 0x3D, 0x85, 0x14, 0x12, 0x1E, 0x4B, 0x5A, 0xC5, 0xAA, 0x32,
	0x76, 0x02, 0x1D, 0x29, 0x09, 0xC7, 0x48, 0x29, 0xED, 0xEB, 0xC6, 0x8D,
98
99
100
101
102
103
104


105
106
107
108
109
110
111
112


113
114




115
116
117
118
119
120
121
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119


120
121
122
123
124
125
126
127
128
129
130







+
+








+
+
-
-
+
+
+
+







	0x77, 0xD6, 0x57, 0x62, 0x38, 0x65, 0x7B, 0x20, 0x3B, 0x19, 0xCA, 0x42,
	0xC1, 0x8A, 0x04, 0x97, 0xF1, 0x6B, 0x48, 0x44, 0xE3, 0x07, 0x4A, 0xE8,
	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
};
#endif
/*
/* The third test vector is too expensive for m68k. */
#ifndef OF_M68K
 * 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,
	0xE6, 0x1E, 0x85, 0xDC, 0x0D, 0x65, 0x1E, 0x40, 0xDF, 0xCF, 0x01, 0x7B,
	0x45, 0x57, 0x58, 0x87
129
130
131
132
133
134
135
136
137


138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160



























161
162
163
164
165
166
167
168
169
170


171
172
173
174










175
176
177
178
179
180
181
182
183
184


185
186
187
188
189
190














191
192
193
194
195
196
197
198
199
200


201


202
203
204

205



206
207


208
209
210
211
212
213
214
215
216
217
218

219
220
221

222

223
138
139
140
141
142
143
144


145
146
147

148



149
















150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188




189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210






211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236

237
238
239
240

241
242
243
244
245


246
247
248
249
250
251
252
253
254
255
256
257

258

259

260
261
262
263







-
-
+
+

-

-
-
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+










+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+
+
-
+
+


-
+

+
+
+
-
-
+
+










-
+
-

-
+

+

	0xAB, 0xE5, 0xEE, 0x98, 0x20, 0xAD, 0xAA, 0x47, 0x8E, 0x56, 0xFD, 0x8F,
	0x4B, 0xA5, 0xD0, 0x9F, 0xFA, 0x1C, 0x6D, 0x92, 0x7C, 0x40, 0xF4, 0xC3,
	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];
	uint32_t blockMixBuffer[32];
	uint32_t ROMixBuffer[32], ROMixTmp[17 * 32];
	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){
	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];

	OFScrypt((OFScryptParameters){
		.blockSize             = 1,
		.costFactor            = 16,
		.parallelization       = 1,
		.salt                  = (unsigned char *)"",
		.saltLength            = 0,
		.password              = "",
		.passwordLength        = 0,
		.key                   = output,
		.keyLength             = 64,
		.allowsSwappableMemory = true
	});

	    })) && memcmp(output, testVector1, 64) == 0)

	TEST(@"scrypt test vector #2",
	    R(OFScrypt((OFScryptParameters){
	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];

	OFScrypt((OFScryptParameters){
		.blockSize             = 8,
		.costFactor            = 1024,
		.parallelization       = 16,
		.salt                  = (unsigned char *)"NaCl",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = output,
		.keyLength             = 64,
		.allowsSwappableMemory = true
	});

	    })) && memcmp(output, testVector2, 64) == 0)

	/* The third test vector is too expensive for m68k. */
#ifndef OF_M68K
	TEST(@"scrypt test vector #3",
	    R(OFScrypt((OFScryptParameters){
	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];

	OFScrypt((OFScryptParameters){
		.blockSize             = 8,
		.costFactor            = 16384,
		.parallelization       = 1,
		.salt                  = (unsigned char *)"SodiumChloride",
		.saltLength            = 14,
		.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
- (void)testRFC7941TestVector4
{
	unsigned char output[64];
	TEST(@"scrypt test vector #4",
	    R(OFScrypt((OFScryptParameters){

	OFScrypt((OFScryptParameters){
		.blockSize             = 8,
		.costFactor            = 1048576,
		.parallelization       = 1,
		.salt                  = (unsigned char *)"SodiumChloride",
		.saltLength            = 14,
		.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 version [bf9db612f3].


























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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

Modified tests/OFSetTests.m from [4668cbb71a] to [7ee6122d59].

11
12
13
14
15
16
17
18

19


20
21
22
23
24
25
26


















27
28
29
30
31
32






33


34
35







36
37
38
39
40








41

42
43
44
45
46
47
48
49






























50
51
52
53
54
55























56
57
58
59





60
61
62











63
64
65

66
67
68


69

70














71


72

73
74
75
76
77










78




79
80
81
82












83
84
85
86
87

88
89
90
91
92
93
94
95
11
12
13
14
15
16
17

18
19
20
21







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40





41
42
43
44
45
46
47
48
49


50
51
52
53
54
55
56
57




58
59
60
61
62
63
64
65
66
67








68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98





99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121




122
123
124
125
126



127
128
129
130
131
132
133
134
135
136
137
138
139

140
141
142

143
144
145
146

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

164





165
166
167
168
169
170
171
172
173
174
175
176
177
178
179




180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

196

197
198
199
200
201
202
203







-
+

+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+

+
+
-
-
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+


-
+


-
+
+

+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+




-
+
-







 * 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 "TestsAppDelegate.h"
#import "OFSetTests.h"

@interface CustomSet: OFSet
{
#import "OFSet.h"
#import "OFConcreteSet.h"
#import "OFConcreteMutableSet.h"

static OFString *module;

@interface SimpleSet: 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
{
	OFMutableSet *_set;
}
@end

@interface SimpleMutableSet: OFMutableSet
	[_set release];

	[super dealloc];
}

- (void)testSetWithArray
{
	OTAssertEqualObjects([self.setClass setWithArray:
	    ([OFArray arrayWithObjects: @"foo", @"bar", @"baz", @"foo", nil])],
	OFMutableSet *_set;
	unsigned long _mutations;
	    _set);
}

- (void)testIsEqual
{
	OTAssertEqualObjects(_set,
	    ([OFSet setWithObjects: @"foo", @"bar", @"baz", nil]));
}
@end

@implementation SimpleSet
- (instancetype)init

- (void)testHash
{
	OTAssertEqual(_set.hash,
	    [([OFSet setWithObjects: @"foo", @"bar", @"baz", nil]) hash]);
}

- (void)testDescription
{
	OFString *description = _set.description;
	self = [super init];

	@try {
		_set = [[OFMutableSet alloc] init];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	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]);
}

	return self;
}

- (instancetype)initWithSet: (OFSet *)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);
}

	self = [super init];

	@try {
		_set = [[OFMutableSet alloc] initWithSet: set];
- (void)testFastEnumeration
{
	bool seenFoo = false, seenBar = false, seenBaz = false;

	for (OFString *object in _set) {
	} @catch (id e) {
		[self release];
		@throw e;
		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);
	}

	return self;
	OTAssert(seenFoo && seenBar && seenBaz);
}

- (instancetype)initWithArray: (OFArray *)array
#ifdef OF_HAVE_BLOCKS
- (void)testEnumerateObjectsUsingBlock
{
	__block bool seenFoo = false, seenBar = false, seenBaz = false;
	self = [super init];

	[_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);
}
	@try {

		_set = [[OFMutableSet alloc] initWithArray: array];
	} @catch (id e) {
		[self release];
		@throw e;
	}
- (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"];
	return self;
}

- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments

	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 = [[OFMutableSet alloc] initWithObject: firstObject
		_set = [[OFSet alloc] initWithObjects: objects count: count];
						  arguments: arguments];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
219
220
221
222
223
224
225
226

























































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
	return [_set containsObject: object];
}

- (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

Modified tests/OFSocketTests.m from [2289bf230f] to [a25ea91bad].

11
12
13
14
15
16
17
18





19
20
21
22
23
24
25
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29







-
+
+
+
+
+







 * 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 "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) &&	\
	a.sockaddr.in6.sin6_addr.s6_addr[3] == (a1 & 0xFF) &&	\
	a.sockaddr.in6.sin6_addr.s6_addr[4] == (a2 >> 8) &&	\
48
49
50
51
52
53
54
55
56
57
58


59
60
61

62
63
64
65
66
67
68
69









70
71
72


73
74
75


76
77
78


79
80
81


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96





















97





98
99
100
101




102
103
104
105
106



107
108
109
110
111



112
113
114
115
116



117
118
119
120
121
122




123
124
125
126
127



128
129
130
131
132
133
134
135









136
137
138


139
140
141


142
143
144


145
146
147


148
149
150


151
152
153


154
155
156


157

158
159
160
161
162









163
164
165
166
167
168







169
170

171
172

173
174

175
176

177
178

179
180

181


182
183
184





185
186

187
188
189


190

191

192
193
194


195
196
197
198
199
200













201
202
203
204
205
206




207
208
209
210
211
212
213
214
215



















216
217
52
53
54
55
56
57
58




59
60
61


62
63







64
65
66
67
68
69
70
71
72
73


74
75
76


77
78
79


80
81
82


83
84
85














86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112




113
114
115
116
117




118
119
120
121




122
123
124
125




126
127
128
129





130
131
132
133
134




135
136
137
138







139
140
141
142
143
144
145
146
147
148


149
150
151


152
153
154


155
156
157


158
159
160


161
162
163


164
165
166


167
168
169
170





171
172
173
174
175
176
177
178
179
180





181
182
183
184
185
186
187


188
189

190


191
192

193


194
195

196
197
198
199



200
201
202
203
204
205

206



207
208
209
210

211



212
213
214





215
216
217
218
219
220
221
222
223
224
225
226
227






228
229
230
231









232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252







-
-
-
-
+
+

-
-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
-
-
-
+
+
+
+

-
-
-
-
+
+
+

-
-
-
-
+
+
+

-
-
-
-
+
+
+

-
-
-
-
-
+
+
+
+

-
-
-
-
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

+
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+
+
-
-
+

-
+
-
-
+

-
+
-
-
+

-
+

+
+
-
-
-
+
+
+
+
+

-
+
-
-
-
+
+

+
-
+
-
-
-
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


	a.sockaddr.in6.sin6_addr.s6_addr[10] = a5 >> 8;		\
	a.sockaddr.in6.sin6_addr.s6_addr[11] = a5 & 0xFF;	\
	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
@implementation OFSocketTests
- (void)testParseIPv4
{
	void *pool = objc_autoreleasePoolPush();
	OFSocketAddress addr;
	OFSocketAddress address = OFSocketAddressParseIP(@"127.0.0.1", 1234);

	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))
	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);

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #2", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"127.0.0.256", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"127.0.0.256", 1234),
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #3", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"127.0.0. 1", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"127.0.0. 1", 1234),
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #4", OFInvalidFormatException,
	    OFSocketAddressParseIP(@" 127.0.0.1", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@" 127.0.0.1", 1234),
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #5", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"127.0.a.1", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"127.0.a.1", 1234),
	    OFInvalidFormatException);

	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)
	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);
	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)

	address = OFSocketAddressParseIP(@"::", 1234);
	OTAssert(COMPARE_V6(address, 0, 0, 0, 0, 0, 0, 0, 0));
	OTAssertEqual(OFFromBigEndian16(address.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)
	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);

	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)
	address = OFSocketAddressParseIP(@"aaAa::", 1234);
	OTAssert(COMPARE_V6(address, 0xAAAA, 0, 0, 0, 0, 0, 0, 0));
	OTAssertEqual(OFFromBigEndian16(address.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)
	address = OFSocketAddressParseIP(@"::aaAa", 1234);
	OTAssert(COMPARE_V6(address, 0, 0, 0, 0, 0, 0, 0, 0xAAAA));
	OTAssertEqual(OFFromBigEndian16(address.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)
	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);

	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)
	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);

	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))
	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);

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #2", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"1: ::2", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1: ::2", 1234),
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #3", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"1:: :2", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1:: :2", 1234),
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #4", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"1::2::3", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1::2::3", 1234),
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #5", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"10000::1", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"10000::1", 1234),
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #6", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"::10000", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"::10000", 1234),
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #7", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"::1::", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"::1::", 1234),
	    OFInvalidFormatException);

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #8", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"1:2:3:4:5:6:7:", 1234))
	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1:2:3:4:5:6:7:", 1234),
	    OFInvalidFormatException);

	OTAssertThrowsSpecific(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))
	    OFInvalidFormatException);

	OTAssertThrowsSpecific(OFSocketAddressParseIP(@"1:2", 1234),
	    OFInvalidFormatException);
}

- (void)testPortForIPv6
{
	OFSocketAddress address = OFSocketAddressParseIP(@"::", 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)
	OTAssertEqual(OFSocketAddressIPPort(&address), 1234);
}

- (void)testStringForIPv6
{
	OFSocketAddress address = OFSocketAddressParseIP(@"::", 1234);

	TEST(@"Converting an IPv6 to a string #1",
	    [OFSocketAddressString(&addr) isEqual: @"::"])
	OTAssertEqualObjects(OFSocketAddressString(&address), @"::");

	SET_V6(addr, 0, 0, 0, 0, 0, 0, 0, 1)
	SET_V6(address, 0, 0, 0, 0, 0, 0, 0, 1)
	TEST(@"Converting an IPv6 to a string #2",
	    [OFSocketAddressString(&addr) isEqual: @"::1"])
	OTAssertEqualObjects(OFSocketAddressString(&address), @"::1");

	SET_V6(addr, 1, 0, 0, 0, 0, 0, 0, 0)
	SET_V6(address, 1, 0, 0, 0, 0, 0, 0, 0)
	TEST(@"Converting an IPv6 to a string #3",
	    [OFSocketAddressString(&addr) isEqual: @"1::"])
	OTAssertEqualObjects(OFSocketAddressString(&address), @"1::");

	SET_V6(addr,
	SET_V6(address,
	    0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00)
	OTAssertEqualObjects(OFSocketAddressString(&address),
	    @"1122:3344:5566:7788:99aa:bbcc:ddee:ff00");
	TEST(@"Converting an IPv6 to a string #4",
	    [OFSocketAddressString(&addr)
	    isEqual: @"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(addr, 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0)
	SET_V6(address, 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0, 0)
	TEST(@"Converting an IPv6 to a string #5",
	    [OFSocketAddressString(&addr)
	    isEqual: @"1122:3344:5566:7788:99aa:bbcc:ddee:0"])
	OTAssertEqualObjects(OFSocketAddressString(&address),
	    @"1122:3344:5566:7788:99aa:bbcc::");

	SET_V6(address,
	SET_V6(addr, 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0, 0)
	    0, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00)
	TEST(@"Converting an IPv6 to a string #6",
	    [OFSocketAddressString(&addr)
	    isEqual: @"1122:3344:5566:7788:99aa:bbcc::"])
	OTAssertEqualObjects(OFSocketAddressString(&address),
	    @"0:3344:5566:7788:99aa:bbcc:ddee:ff00");

	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(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;
	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)
	OTAssertEqualObjects(OFSocketAddressString(&address),
	    @"::5566:7788:99aa:bbcc:0:0%123");
}

	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);
- (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

Modified tests/OFStreamTests.m from [b1e1e0bf5b] to [cac1034b86].

11
12
13
14
15
16
17
18


19
20


21
22

23
24


























25
26
27
28

29
30
31

32
33
34
35
36
37
38

39
40
41
42
43
44
45

46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
11
12
13
14
15
16
17

18
19
20

21
22
23

24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57

58
59
60
61
62
63
64

65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80

81
82
83
84
85
86
87

































-
+
+

-
+
+

-
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+


-
+






-
+






-
+








-
+






-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFStream";
@interface OFStreamTests: OTTestCase
@end

@interface StreamTest: OFStream
@interface OFTestStream: OFStream
{
	int state;
	int _state;
}
@end

@implementation OFStreamTests
- (void)testStream
{
	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 version [358687fcb0].


























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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

Modified tests/OFStringTests.m from [dc9aaa2341] to [0fac0482da].

15
16
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58

59
60
61
62
63






































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































64

65
66
67
68
69
70
71
15
16
17
18
19
20
21



22


23
24
25
26
27





28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532

1533
1534
1535
1536
1537
1538
1539
1540







-
-
-
+
-
-





-
-
-
-
-















-
+





-
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+








#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <math.h>

#import "TestsAppDelegate.h"

#import "OFString.h"
#import "OFStringTests.h"
#import "OFMutableUTF8String.h"
#import "OFUTF8String.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,
	0x72000000, 0x3AF00100, 0
};
static const OFChar16 char16String[] = {
	0xFEFF, 'f', 0xF6, 0xF6, 'b', 0xE4, 'r', 0xD83C, 0xDC3A, 0
};
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

@interface EntityHandler: OFObject <OFStringXMLUnescapingDelegate>
@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:
	    @"<hello> &world'\"!&"] stringByXMLEscaping],
	    @"&lt;hello&gt; &amp;world&apos;&quot;!&amp;");
}

- (void)testStringByXMLUnescaping
{
	OTAssertEqualObjects([[self.stringClass stringWithString:
	    @"&lt;hello&gt; &amp;world&apos;&quot;!&amp;"]
	    stringByXMLUnescaping],
	    @"<hello> &world'\"!&");

	OTAssertEqualObjects([[self.stringClass stringWithString: @"&#x79;"]
	    stringByXMLUnescaping], @"y");
	OTAssertEqualObjects([[self.stringClass stringWithString: @"&#xe4;"]
	    stringByXMLUnescaping], @"ä");
	OTAssertEqualObjects([[self.stringClass stringWithString: @"&#8364;"]
	    stringByXMLUnescaping], @"€");
	OTAssertEqualObjects([[self.stringClass stringWithString: @"&#x1D11E;"]
	    stringByXMLUnescaping], @"ð„ž");
}

- (void)testStringByXMLUnescapingThrowsOnUnknownEntities
{
	OTAssertThrowsSpecific([[self.stringClass stringWithString: @"&foo;"]
	    stringByXMLUnescaping], OFUnknownXMLEntityException);
}

- (void)testStringByXMLUnescapingThrowsOnInvalidFormat
{
	OTAssertThrowsSpecific([[self.stringClass stringWithString: @"x&amp"]
	    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 SimpleString
@implementation CustomString
- (instancetype)init
{
	self = [super init];

	@try {
		_string = [[OFMutableString alloc] init];
	} @catch (id e) {
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1645
1646
1647
1648
1649
1650
1651

















1652
1653
1654
1655
1656
1657
1658
1659
1660



































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































1661







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-









-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


- (size_t)length
{
	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 <OFStringXMLUnescapingDelegate>
@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(@"<hello> &world'\"!&").stringByXMLEscaping) &&
	    [string isEqual: @"&lt;hello&gt; &amp;world&apos;&quot;!&amp;"])

	TEST(@"-[stringByXMLUnescaping]",
	    [string.stringByXMLUnescaping isEqual: @"<hello> &world'\"!&"] &&
	    [C(@"&#x79;").stringByXMLUnescaping isEqual: @"y"] &&
	    [C(@"&#xe4;").stringByXMLUnescaping isEqual: @"ä"] &&
	    [C(@"&#8364;").stringByXMLUnescaping isEqual: @"€"] &&
	    [C(@"&#x1D11E;").stringByXMLUnescaping isEqual: @"ð„ž"])

	EXPECT_EXCEPTION(@"Detect unknown entities in -[stringByXMLUnescaping]",
	    OFUnknownXMLEntityException, [C(@"&foo;") stringByXMLUnescaping])
	EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] "
	    @"#1", OFInvalidFormatException,
	    [C(@"x&amp") 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

Modified tests/OFSubprocessTests.m from [846e8d88e2] to [c950e491df].

11
12
13
14
15
16
17
18


19
20


21
22
23


24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44




45
46
47


48
49




50
51

52
53
54


55
56
57
11
12
13
14
15
16
17

18
19
20

21
22
23


24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
39






40
41
42
43
44


45
46


47
48
49
50
51

52
53


54
55


56







-
+
+

-
+
+

-
-
+
+

-













-
-
-
-
-
-
+
+
+
+

-
-
+
+
-
-
+
+
+
+

-
+

-
-
+
+
-
-

 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFSubprocess";
@interface OFSubprocessTests: OTTestCase
@end

@implementation TestsAppDelegate (OFSubprocessTests)
- (void)subprocessTests
@implementation OFSubprocessTests
- (void)testSubprocess
{
	void *pool = objc_autoreleasePoolPush();
#ifdef OF_HAVE_FILES
	OFString *program = [@"subprocess" stringByAppendingPathComponent:
	    @"subprocess" @PROG_SUFFIX];
#else
	OFString *program = @"subprocess/subprocess" @PROG_SUFFIX;
#endif
	OFArray *arguments = [OFArray arrayWithObjects: @"tést", @"123", nil];
	OFMutableDictionary *environment =
	    [[[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]))
	subprocess = [OFSubprocess subprocessWithProgram: program
					     programName: program
					       arguments: arguments
					     environment: environment];

	TEST(@"Standard input", R([subprocess writeLine: @"Hellö world!"]))

	[subprocess writeLine: @"Hellö world!"];
#ifdef OF_HAVE_UNICODE_TABLES
	TEST(@"Standard output",
	    [[subprocess readLine] isEqual: @"HELLÖ WORLD!"])
	OTAssertEqualObjects([subprocess readLine], @"HELLÖ WORLD!");
#else
	OTAssertEqualObjects([subprocess readLine], @"HELLö WORLD!");
#endif

	TEST(@"-[closeForWriting]", R([subprocess closeForWriting]))
	[subprocess closeForWriting];

	TEST(@"-[waitForTermination]", [subprocess waitForTermination] == 0)

	OTAssertEqual([subprocess waitForTermination], 0);
}
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFSystemInfoTests.m from [f3db5fbea9] to [0545403f54].

11
12
13
14
15
16
17
18





19
20
21
22

23
24
25
26
27
28
29
30

31
32
33
34

35
36
37
38
39
40


41
42

43
44

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

81
82
83

84
85
86
87


88
89
90

91
92
93

94
95
96

97
98
99

100
101

102
103
104
105
106
107
108





109
110
111
112
113
114





115
116
117


118
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167



































168
169
170

171
172

173
174
175

176
177

178
179
180

181
182

183
184
185

186
187

188
189
190
191


192
193
194
195


196
197
198
199


200
201
202
203
204

205
206





207
208
209

210
211
212
213
214
215
216

217
218
219
220

221
222
223

224
225


226

227
228


229
230

231
232


233
234

235

236
237
238
239
240
241
242
243
244
245
246

247
248
249

250

251

252

253
254
255
256

257


258

259
260
261

262
263
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33

34
35
36
37

38
39
40
41
42


43
44
45

46
47
48
49
50
51






























52



53



54




55
56



57



58



59



60


61
62






63
64
65
66
67






68
69
70
71
72



73
74


75






















76

























77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111



112


113



114


115



116


117



118


119




120
121




122
123




124
125
126
127
128


129
130
131
132
133
134
135
136
137
138

139
140
141
142
143
144
145

146
147
148
149

150
151
152
153
154


155
156
157
158


159
160
161
162
163


164
165
166
167
168

169
170
171
172
173
174
175
176
177
178
179

180
181
182
183
184

185
186
187

188
189
190
191

192
193
194
195

196
197
198

199
200
201







-
+
+
+
+
+



-
+







-
+



-
+




-
-
+
+

-
+


+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
-
-
+
-
-
-
+
-
-
-
-
+
+
-
-
-
+
-
-
-
+
-
-
-
+
-
-
-
+
-
-
+

-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
-
+
-
-
-
+
-
-
+
-
-
-
+
-
-
+
-
-
-
+
-
-
+
-
-
-
-
+
+
-
-
-
-
+
+
-
-
-
-
+
+



-
-
+


+
+
+
+
+


-
+






-
+



-
+



+
-
-
+
+

+
-
-
+
+


+
-
-
+
+


+
-
+










-
+



+
-
+

+
-
+



-
+

+
+
-
+


-
+


 * 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 "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: @", "];
			[string appendString: @", "];

		*firstAddress = false;

		[OFStdOut writeString: OFSocketAddressString(address)];
		[string appendString: OFSocketAddressString(address)];
	}
}
#endif

@implementation TestsAppDelegate (OFSystemInfoTests)
- (void)systemInfoTests
@implementation OFSystemInfoTests
+ (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, id) *) *)summary
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray *summary = [OFMutableArray array];
#ifdef OF_HAVE_SOCKETS
	OFDictionary *networkInterfaces;
	OFMutableString *networkInterfacesString;
	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];

#define ADD(name, value)						\
	[OFStdOut writeFormat: @"[OFSystemInfo] CPU vendor: %@\n",
	    [OFSystemInfo CPUVendor]];

	[summary addObject: [OFPair pairWithFirstObject: name		\
	[OFStdOut writeFormat: @"[OFSystemInfo] CPU model: %@\n",
	    [OFSystemInfo CPUModel]];

#if defined(OF_AMD64) || defined(OF_X86)
					   secondObject: value]];
#define ADD_UINT(name, value)						\
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports MMX: %d\n",
	    [OFSystemInfo supportsMMX]];

	ADD(name, [OFNumber numberWithUnsignedInt: value]);
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports 3DNow!: %d\n",
	    [OFSystemInfo supports3DNow]];

#define ADD_ULONGLONG(name, value)					\
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports enhanced 3DNow!: %d\n",
	    [OFSystemInfo supportsEnhanced3DNow]];

	ADD(name, [OFNumber numberWithUnsignedLongLong: value]);
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE: %d\n",
	    [OFSystemInfo supportsSSE]];

#define ADD_BOOL(name, value)						\
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE2: %d\n",
	    [OFSystemInfo supportsSSE2]];
	ADD(name, [OFNumber numberWithBool: value]);

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE3: %d\n",
	    [OFSystemInfo supportsSSE3]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSSE3: %d\n",
	    [OFSystemInfo supportsSSSE3]];

	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]);
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE4.1: %d\n",
	    [OFSystemInfo supportsSSE41]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE4.2: %d\n",
	    [OFSystemInfo supportsSSE42]];

	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",
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports AVX: %d\n",
	    [OFSystemInfo supportsAVX]];

	    [OFSystemInfo temporaryDirectoryIRI].string);
	ADD(@"CPU vendor", [OFSystemInfo CPUVendor]);
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports AVX2: %d\n",
	    [OFSystemInfo supportsAVX2]];
	ADD(@"CPU model", [OFSystemInfo CPUModel]);

	[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]];
#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]);

	[OFStdOut writeFormat:
	    @"[OFSystemInfo] Supports AVX-512 Vector Byte Manipulation "
	ADD_BOOL(@"Supports AVX-512 Vector Byte Manipulation Instructions",
	    @"Instructions: %d\n",
	    [OFSystemInfo supportsAVX512VectorByteManipulationInstructions]];
	    [OFSystemInfo supportsAVX512VectorByteManipulationInstructions]);

	[OFStdOut writeFormat:
	    @"[OFSystemInfo] Supports AVX-512 Vector Population Count "
	ADD_BOOL(@"Supports AVX-512 Vector Population Count Instruction",
	    @"Instruction: %d\n",
	    [OFSystemInfo supportsAVX512VectorPopulationCountInstruction]];
	    [OFSystemInfo supportsAVX512VectorPopulationCountInstruction]);

	[OFStdOut writeFormat:
	    @"[OFSystemInfo] Supports AVX-512 Vector Neutral Network "
	ADD_BOOL(@"Supports AVX-512 Vector Neutral Network Instructions",
	    @"Instructions: %d\n",
	    [OFSystemInfo supportsAVX512VectorNeuralNetworkInstructions]];
	    [OFSystemInfo supportsAVX512VectorNeuralNetworkInstructions]);

	[OFStdOut writeFormat:
	    @"[OFSystemInfo] Supports AVX-512 Vector Byte Manipulation "
	ADD_BOOL(@"Supports AVX-512 Vector Byte Manipulation Instructions 2",
	    @"Instructions 2: %d\n",
	    [OFSystemInfo supportsAVX512VectorByteManipulationInstructions2]];
	    [OFSystemInfo supportsAVX512VectorByteManipulationInstructions2]);

	[OFStdOut writeFormat:
	    @"[OFSystemInfo] Supports AVX-512 Bit Algorithms: %d\n",
	    [OFSystemInfo supportsAVX512BitAlgorithms]];
	ADD_BOOL(@"Supports AVX-512 Bit Algorithms",
	    [OFSystemInfo supportsAVX512BitAlgorithms]);

	[OFStdOut writeFormat:
	    @"[OFSystemInfo] Supports AVX-512 Float16 Instructions: %d\n",
	    [OFSystemInfo supportsAVX512Float16Instructions]];
	ADD_BOOL(@"Supports AVX-512 Float16 Instructions",
	    [OFSystemInfo supportsAVX512Float16Instructions]);

	[OFStdOut writeFormat:
	    @"[OFSystemInfo] Supports AVX-512 BFloat16 Instructions: %d\n",
	    [OFSystemInfo supportsAVX512BFloat16Instructions]];
	ADD_BOOL(@"Supports AVX-512 BFloat16 Instructions",
	    [OFSystemInfo supportsAVX512BFloat16Instructions]);
#endif

#ifdef OF_POWERPC
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports AltiVec: %d\n",
	    [OFSystemInfo supportsAltiVec]];
	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];
	[OFStdOut writeString: @"[OFSystemInfo] Network interfaces: "];
	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];

		appendAddresses(networkInterfacesString,
		printAddresses([interface objectForKey:
		    OFNetworkInterfaceIPv4Addresses], &firstAddress);
		    [interface objectForKey: OFNetworkInterfaceIPv4Addresses],
		    &firstAddress);
# ifdef OF_HAVE_IPV6
		appendAddresses(networkInterfacesString,
		printAddresses([interface objectForKey:
		    OFNetworkInterfaceIPv6Addresses], &firstAddress);
		    [interface objectForKey: OFNetworkInterfaceIPv6Addresses],
		    &firstAddress);
# endif
# ifdef OF_HAVE_IPX
		appendAddresses(networkInterfacesString,
		printAddresses([interface objectForKey:
		    OFNetworkInterfaceIPXAddresses], &firstAddress);
		    [interface objectForKey: OFNetworkInterfaceIPXAddresses],
		    &firstAddress);
# endif
# ifdef OF_HAVE_APPLETALK
		appendAddresses(networkInterfacesString,
		printAddresses([interface objectForKey:
		    [interface objectForKey:
		    OFNetworkInterfaceAppleTalkAddresses], &firstAddress);
# endif

		hardwareAddress = [interface
		    objectForKey: OFNetworkInterfaceHardwareAddress];
		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)
					[networkInterfacesString
					[OFStdOut writeString: @":"];
					    appendString: @":"];

				[networkInterfacesString
				[OFStdOut writeFormat: @"%02X", bytes[i]];
				    appendFormat: @"%02X", bytes[i]];
			}
		}

		[OFStdOut writeString: @")"];
		[networkInterfacesString appendString: @")"];
	}
	[summary addObject:
	    [OFPair pairWithFirstObject: @"Network interfaces"
	[OFStdOut writeString: @"\n"];
			   secondObject: networkInterfacesString]];
#endif

	objc_autoreleasePoolPop(pool);
	return summary;
}
@end

Modified tests/OFTCPSocketTests.m from [4cfbc85ac3] to [5aca96f5ad].

13
14
15
16
17
18
19
20


21
22


23
24
25


26
27
28

29
30
31
32
33


34
35
36

37
38

39
40
41
42


43
44

45
46
47
48


49
50

51
52
53
54


55
56
57
58
13
14
15
16
17
18
19

20
21
22

23
24
25


26
27
28


29
30
31
32


33
34
35


36


37
38



39
40
41

42




43
44
45

46
47



48
49


50
51







-
+
+

-
+
+

-
-
+
+

-
-
+



-
-
+
+

-
-
+
-
-
+

-
-
-
+
+

-
+
-
-
-
-
+
+

-
+

-
-
-
+
+
-
-


 * file.
 */

#include "config.h"

#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFTCPSocket";
@interface OFTCPSocketTests: OTTestCase
@end

@implementation TestsAppDelegate (OFTCPSocketTests)
- (void)TCPSocketTests
@implementation OFTCPSocketTests
- (void)testTCPSocket
{
	void *pool = objc_autoreleasePoolPush();
	OFTCPSocket *server, *client = nil, *accepted;
	OFTCPSocket *server, *client, *accepted;
	OFSocketAddress address;
	char buffer[6];

	TEST(@"+[socket]", (server = [OFTCPSocket socket]) &&
	    (client = [OFTCPSocket socket]))
	server = [OFTCPSocket socket];
	client = [OFTCPSocket socket];

	TEST(@"-[bindToHost:port:]",
	    R(address = [server bindToHost: @"127.0.0.1" port: 0]))
	address = [server bindToHost: @"127.0.0.1" port: 0];

	TEST(@"-[listen]", R([server listen]))
	[server listen];

	TEST(@"-[connectToHost:port:]",
	    R([client connectToHost: @"127.0.0.1"
			       port: OFSocketAddressIPPort(&address)]))
	[client connectToHost: @"127.0.0.1"
			 port: OFSocketAddressIPPort(&address)];

	TEST(@"-[accept]", (accepted = [server accept]))
	accepted = [server accept];

	TEST(@"-[remoteAddress]",
	    [OFSocketAddressString(accepted.remoteAddress)
	    isEqual: @"127.0.0.1"])
	OTAssertEqualObjects(OFSocketAddressString(accepted.remoteAddress),
	    @"127.0.0.1");

	TEST(@"-[writeString:]", R([client writeString: @"Hello!"]))
	[client writeString: @"Hello!"];

	TEST(@"-[readIntoBuffer:length:]",
	    [accepted readIntoBuffer: buffer length: 6] &&
	    !memcmp(buffer, "Hello!", 6))
	[accepted readIntoBuffer: buffer exactLength: 6];
	OTAssertEqual(memcmp(buffer, "Hello!", 6), 0);

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFThreadTests.m from [bbeb8a5fbf] to [b2b1434e44].

11
12
13
14
15
16
17
18


19
20


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37


38
39
40

41
42
43
44

45
46
47

48
49
50


51
52
53
11
12
13
14
15
16
17

18
19
20

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37


38
39
40


41
42



43
44


45



46
47


48







-
+
+

-
+
+















-
-
+
+

-
-
+

-
-
-
+

-
-
+
-
-
-
+
+
-
-

 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFThread";
@interface OFThreadTests: OTTestCase
@end

@interface TestThread: OFThread
@end

@implementation TestThread
- (id)main
{
	[[OFThread threadDictionary] setObject: @"bar" forKey: @"foo"];
	OFEnsure([[[OFThread threadDictionary]
	    objectForKey: @"foo"] isEqual: @"bar"]);

	return @"success";
}
@end

@implementation TestsAppDelegate (OFThreadTests)
- (void)threadTests
@implementation OFThreadTests
- (void)testThread
{
	void *pool = objc_autoreleasePoolPush();
	TestThread *thread;
	TestThread *thread = [TestThread thread];

	TEST(@"+[thread]", (thread = [TestThread thread]))

	TEST(@"-[start]", R([thread start]))
	[thread start];

	TEST(@"-[join]", [[thread join] isEqual: @"success"])

	OTAssertEqualObjects([thread join], @"success");
	TEST(@"-[threadDictionary]",
	    [[OFThread threadDictionary] objectForKey: @"foo"] == nil)

	OTAssertNil([[OFThread threadDictionary] objectForKey: @"foo"]);
}
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFUDPSocketTests.m from [9c08c02308] to [b806dc5c9a].

13
14
15
16
17
18
19
20


21
22


23
24
25


26
27
28
29
30



31
32

33
34
35


36
37
38

39
40
41
42
43
44




45
46
47
48


49
50
51
52
53
54
55
56
57
58
59
13
14
15
16
17
18
19

20
21
22

23
24
25


26
27
28




29
30
31
32

33
34


35
36
37


38
39





40
41
42
43




44
45










46







-
+
+

-
+
+

-
-
+
+

-
-
-
-
+
+
+

-
+

-
-
+
+

-
-
+

-
-
-
-
-
+
+
+
+
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-

 * file.
 */

#include "config.h"

#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFUDPSocket";
@interface OFUDPSocketTests: OTTestCase
@end

@implementation TestsAppDelegate (OFUDPSocketTests)
- (void)UDPSocketTests
@implementation OFUDPSocketTests
- (void)testUDPSocket
{
	void *pool = objc_autoreleasePoolPush();
	OFUDPSocket *sock;
	OFSocketAddress addr1, addr2, addr3;
	char buf[6];
	OFUDPSocket *sock = [OFUDPSocket socket];
	OFSocketAddress addr1, addr2;
	char buffer[6];

	TEST(@"+[socket]", (sock = [OFUDPSocket socket]))
	sock = [OFUDPSocket socket];

	TEST(@"-[bindToHost:port:]",
	    R(addr1 = [sock bindToHost: @"127.0.0.1" port: 0]))
	addr1 = [sock bindToHost: @"127.0.0.1" port: 0];
	OTAssertEqualObjects(OFSocketAddressString(&addr1), @"127.0.0.1");

	TEST(@"-[sendBuffer:length:receiver:]",
	    R([sock sendBuffer: "Hello" length: 6 receiver: &addr1]))
	[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))
	[sock receiveIntoBuffer: buffer length: 6 sender: &addr2];
	OTAssertEqual(memcmp(buffer, "Hello", 6), 0);
	OTAssertEqualObjects(OFSocketAddressString(&addr2), @"127.0.0.1");
	OTAssertEqual(OFSocketAddressIPPort(&addr2),

	addr3 = OFSocketAddressParseIP(@"127.0.0.1",
	    OFSocketAddressIPPort(&addr1) + 1);

	    OFSocketAddressIPPort(&addr1));
}
	TEST(@"OFSocketAddressEqual()",
	    OFSocketAddressEqual(&addr1, &addr2) &&
	    !OFSocketAddressEqual(&addr1, &addr3))

	TEST(@"OFSocketAddressHash()",
	    OFSocketAddressHash(&addr1) == OFSocketAddressHash(&addr2) &&
	    OFSocketAddressHash(&addr1) != OFSocketAddressHash(&addr3))

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFUNIXDatagramSocketTests.m from [588aa866e4] to [cd285c23b4].

12
13
14
15
16
17
18

19
20


21
22


23
24
25


26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80
81







82
83
84
85
86
87

88
89
90
12
13
14
15
16
17
18
19
20

21
22
23

24
25
26


27
28
29

30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50


51

52
53
54
55
56




57




58
59
60
61
62
63


64
65








66
67
68
69
70
71
72
73
74
75
76
77

78


79







+

-
+
+

-
+
+

-
-
+
+

-
+

-



















-
-

-
+




-
-
-
-
+
-
-
-
-






-
-
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+





-
+
-
-

 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <errno.h>
#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFUNIXDatagramSocket";
@interface OFUNIXDatagramSocketTests: OTTestCase
@end

@implementation TestsAppDelegate (OFUNIXDatagramSocketTests)
- (void)UNIXDatagramSocketTests
@implementation OFUNIXDatagramSocketTests
- (void)testUNIXDatagramSocket
{
	void *pool = objc_autoreleasePoolPush();
	OFUNIXDatagramSocket *sock = [OFUNIXDatagramSocket socket];
	OFString *path;
	OFUNIXDatagramSocket *sock;
	OFSocketAddress address1, address2;
	char buffer[5];

#if defined(OF_HAVE_FILES) && !defined(OF_IOS)
	path = [[OFSystemInfo temporaryDirectoryIRI]
	    IRIByAppendingPathComponent: [[OFUUID UUID] UUIDString]]
	    .fileSystemRepresentation;
#else
	/*
	 * We can have sockets, including UNIX sockets, while file support is
	 * disabled.
	 *
	 * We also use this code path for iOS, as the temporaryDirectoryIRI is
	 * too long on the iOS simulator.
	 */
	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 "
			OTSkip(@"UNIX datagram sockets unsupported");
			    @"tests"];

			objc_autoreleasePoolPop(pool);
			return;
		default:
			@throw e;
		}
	}

	@try {
		TEST(@"-[sendBuffer:length:receiver:]",
		    R([sock sendBuffer: "Hello" length: 5 receiver: &address1]))
		[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))
		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

Modified tests/OFUNIXStreamSocketTests.m from [b3f4ed0fcd] to [404c597141].

12
13
14
15
16
17
18

19
20


21
22


23
24
25


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49


50
51
52

53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
69
70

71
72
73

74
75

76
77
78

79
80
81
82


83
84
85


86
87
88
89
90
91

92
93
94
12
13
14
15
16
17
18
19
20

21
22
23

24
25
26


27
28
29

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49


50
51
52
53

54
55
56
57
58




59



60
61
62
63
64
65

66
67


68
69

70



71
72



73
74
75


76
77
78
79
80
81
82

83


84







+

-
+
+

-
+
+

-
-
+
+

-




















-
-
+
+


-
+




-
-
-
-
+
-
-
-






-
+

-
-
+

-
+
-
-
-
+

-
-
-
+
+

-
-
+
+





-
+
-
-

 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <errno.h>
#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFUNIXStreamSocket";
@interface OFUNIXStreamSocketTests: OTTestCase
@end

@implementation TestsAppDelegate (OFUNIXStreamSocketTests)
- (void)UNIXStreamSocketTests
@implementation OFUNIXStreamSocketTests
- (void)testUNIXStreamSocket
{
	void *pool = objc_autoreleasePoolPush();
	OFString *path;
	OFUNIXStreamSocket *sockClient, *sockServer, *sockAccepted;
	char buffer[5];

#if defined(OF_HAVE_FILES) && !defined(OF_IOS)
	path = [[OFSystemInfo temporaryDirectoryIRI]
	    IRIByAppendingPathComponent: [[OFUUID UUID] UUIDString]]
	    .fileSystemRepresentation;
#else
	/*
	 * We can have sockets, including UNIX sockets, while file support is
	 * disabled.
	 *
	 * We also use this code path for iOS, as the temporaryDirectory:RI is
	 * too long on the iOS simulator.
	 */
	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"];
			OTSkip(@"UNIX stream sockets unsupported");

			objc_autoreleasePoolPop(pool);
			return;
		default:
			@throw e;
		}
	}

	@try {
		TEST(@"-[listen]", R([sockServer listen]))
		[sockServer listen];

		TEST(@"-[connectToPath:]",
		    R([sockClient connectToPath: path]))
		[sockClient connectToPath: path];

		TEST(@"-[accept]", (sockAccepted = [sockServer accept]))
		sockAccepted = [sockServer accept];

		TEST(@"-[writeBuffer:length:]",
		    R([sockAccepted writeBuffer: "Hello" length: 5]))
		[sockAccepted writeBuffer: "Hello" length: 5];

		TEST(@"-[readIntoBuffer:length:]",
		    [sockClient readIntoBuffer: buffer length: 5] == 5 &&
		    memcmp(buffer, "Hello", 5) == 0)
		OTAssertEqual([sockClient readIntoBuffer: buffer length: 5], 5);
		OTAssertEqual(memcmp(buffer, "Hello", 5), 0);

		TEST(@"-[remoteAddress]", OFSocketAddressUNIXPath(
		    sockAccepted.remoteAddress).length == 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 version [29756ee219].































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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

Modified tests/OFValueTests.m from [52b7d9b170] to [200d6d2788].

13
14
15
16
17
18
19
20


21
22


23
24
25


26



27






28


29
30
31
32
33



















34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

51
52
53
54
55




56
57

58
59
60
61
62
63








64
65
66
67
68




69
70

71
72
73
74
75
76








77
78
79
80
81




82
83
84
85
86
87
88
89
90




91
92
93
94
95








96
97
98
99
100




101
102
103
104
105
106
107
108
109




110
111
112
113
114








115
116
117
118
119




120
121
122
123
124
125


126
127

128
129
130
131
132
133








134
135
136
137
138




139
140
141
142
143
144


145
146
147
148
149
150
151
152
153
154














155
156
157
158




159
160
161
162
13
14
15
16
17
18
19

20
21
22

23
24
25


26
27
28
29
30
31

32
33
34
35
36
37
38
39
40





41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

















60
61




62
63
64
65
66

67

68




69
70
71
72
73
74
75
76
77




78
79
80
81
82

83

84




85
86
87
88
89
90
91
92
93




94
95
96
97






98


99
100
101
102
103




104
105
106
107
108
109
110
111
112




113
114
115
116






117


118
119
120
121
122




123
124
125
126
127
128
129
130
131




132
133
134
135

136




137
138
139

140

141




142
143
144
145
146
147
148
149
150




151
152
153
154

155




156
157
158









159
160
161
162
163
164
165
166
167
168
169
170
171
172




173
174
175
176


177
178







-
+
+

-
+
+

-
-
+
+

+
+
+
-
+
+
+
+
+
+

+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+

-
-
-
-
+
+
+
+

-
+
-

-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+

-
+
-

-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
-
-
-
-
-
-

-
-
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
-
-
-
-
-
-

-
-
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
-

-
-
-
-
+
+

-
+
-

-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
-

-
-
-
-
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-


 * file.
 */

#include "config.h"

#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFValue";
@interface OFValueTests: OTTestCase
@end

@implementation TestsAppDelegate (OFValueTests)
- (void)valueTests
@implementation OFValueTests
- (void)testObjCType
{
	OFRange range = OFMakeRange(1, 64);
	OFValue *value = [OFValue valueWithBytes: &range
					objCType: @encode(OFRange)];
	void *pool = objc_autoreleasePoolPush();

	OTAssertEqual(strcmp(value.objCType, @encode(OFRange)), 0);
}

- (void)testGetValueSize
{
	OFRange range = OFMakeRange(1, 64), range2;
	OFValue *value = [OFValue valueWithBytes: &range
					objCType: @encode(OFRange)];
	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;

	[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;

	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]))
	OFValue *value = [OFValue valueWithPointer: pointer];

	TEST(@"-[pointerValue]",
	    value.pointerValue == pointer &&
	    [[OFValue valueWithBytes: &pointer
			    objCType: @encode(void *)] pointerValue] == pointer)
	OTAssertEqual(value.pointerValue, pointer);
	OTAssertEqual([[OFValue valueWithBytes: &pointer
				      objCType: @encode(void *)] pointerValue],
	    pointer);

	EXPECT_EXCEPTION(@"-[pointerValue] with wrong size throws",
	OTAssertThrowsSpecific(
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] pointerValue])

	TEST(@"+[valueWithNonretainedObject:]",
	    (value = [OFValue valueWithNonretainedObject: pointer]))
			    objCType: @encode(char)] pointerValue],
	    OFOutOfRangeException);
}

- (void)testNonretainedObject
{
	id object = (id)&object;
	OFValue *value = [OFValue valueWithNonretainedObject: object];

	TEST(@"-[nonretainedObjectValue]",
	    value.nonretainedObjectValue == pointer &&
	    [[OFValue valueWithBytes: &pointer
			    objCType: @encode(id)] pointerValue] == pointer)
	OTAssertEqual(value.nonretainedObjectValue, object);
	OTAssertEqual([[OFValue
	    valueWithBytes: &object
		  objCType: @encode(id)] nonretainedObjectValue], object);

	EXPECT_EXCEPTION(@"-[nonretainedObjectValue] with wrong size throws",
	OTAssertThrowsSpecific(
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] nonretainedObjectValue])

	TEST(@"+[valueWithRange:]",
	    (value = [OFValue valueWithRange: range]))
			    objCType: @encode(char)] nonretainedObjectValue],
	    OFOutOfRangeException);
}

- (void)testRange
{
	OFRange range = OFMakeRange(1, 64), range2;
	OFValue *value = [OFValue valueWithRange: range];

	TEST(@"-[rangeValue]",
	    OFEqualRanges(value.rangeValue, range) &&
	    (value = [OFValue valueWithBytes: &range
				    objCType: @encode(OFRange)]) &&
	OTAssert(OFEqualRanges(value.rangeValue, range));
	OTAssert(OFEqualRanges(
	    [[OFValue valueWithBytes: &range
			    objCType: @encode(OFRange)] rangeValue], range));
	    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,
	[value getValue: &range2 size: sizeof(range2)];
	OTAssert(OFEqualRanges(range2, range));

	OTAssertThrowsSpecific(
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] rangeValue])

	TEST(@"+[valueWithPoint:]",
	    (value = [OFValue valueWithPoint: point]))
			    objCType: @encode(char)] rangeValue],
	    OFOutOfRangeException);
}

- (void)testPoint
{
	OFPoint point = OFMakePoint(1.5f, 3.0f), point2;
	OFValue *value = [OFValue valueWithPoint: point];

	TEST(@"-[pointValue]",
	    OFEqualPoints(value.pointValue, point) &&
	    (value = [OFValue valueWithBytes: &point
				    objCType: @encode(OFPoint)]) &&
	OTAssert(OFEqualPoints(value.pointValue, point));
	OTAssert(OFEqualPoints(
	    [[OFValue valueWithBytes: &point
			    objCType: @encode(OFPoint)] pointValue], point));
	    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,
	[value getValue: &point2 size: sizeof(point2)];
	OTAssert(OFEqualPoints(point2, point));

	OTAssertThrowsSpecific(
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] pointValue])

	TEST(@"+[valueWithSize:]",
	    (value = [OFValue valueWithSize: size]))
			    objCType: @encode(char)] pointValue],
	    OFOutOfRangeException);
}

- (void)testSize
{
	OFSize size = OFMakeSize(4.5f, 5.0f), size2;
	OFValue *value = [OFValue valueWithSize: size];

	TEST(@"-[sizeValue]",
	    OFEqualSizes(value.sizeValue, size) &&
	    (value = [OFValue valueWithBytes: &size
				    objCType: @encode(OFSize)]) &&
	OTAssert(OFEqualSizes(value.sizeValue, size));
	OTAssert(OFEqualSizes(
	    [[OFValue valueWithBytes: &size
			    objCType: @encode(OFSize)] sizeValue], size));
	    OFEqualSizes(value.sizeValue, size))

	TEST(@"-[getValue:size:] for OFSizeValue",
	    (value = [OFValue valueWithSize: size]) &&
	    R([value getValue: &size2 size: sizeof(size2)]) &&
	    OFEqualSizes(size2, size))
	[value getValue: &size2 size: sizeof(size2)];
	OTAssert(OFEqualSizes(size2, size));

	EXPECT_EXCEPTION(@"-[sizeValue] with wrong size throws",
	OTAssertThrowsSpecific(
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] sizeValue])

	TEST(@"+[valueWithRect:]",
	    (value = [OFValue valueWithRect: rect]))
			    objCType: @encode(char)] sizeValue],
	    OFOutOfRangeException);
}

- (void)testRect
{
	OFRect rect = OFMakeRect(1.5f, 3.0f, 4.5f, 6.0f), rect2;
	OFValue *value = [OFValue valueWithRect: rect];

	TEST(@"-[rectValue]",
	    OFEqualRects(value.rectValue, rect) &&
	    (value = [OFValue valueWithBytes: &rect
				    objCType: @encode(OFRect)]) &&
	OTAssert(OFEqualRects(value.rectValue, rect));
	OTAssert(OFEqualRects(
	    [[OFValue valueWithBytes: &rect
			    objCType: @encode(OFRect)] rectValue], rect));
	    OFEqualRects(value.rectValue, rect))

	TEST(@"-[getValue:size:] for OFRectValue",
	    (value = [OFValue valueWithRect: rect]) &&
	    R([value getValue: &rect2 size: sizeof(rect2)]) &&
	    OFEqualRects(rect2, rect))
	[value getValue: &rect2 size: sizeof(rect2)];
	OTAssert(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)]
	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)],
	    isEqual: [OFValue valueWithBytes: "a"
				    objCType: @encode(unsigned char)]] &&
	    ![[OFValue valueWithBytes: "a" objCType: @encode(char)]
	    isEqual: [OFValue valueWithBytes: "b" objCType: @encode(char)]])
	    [OFValue valueWithBytes: "a" objCType: @encode(unsigned char)]);
	OTAssertNotEqualObjects(
	    [OFValue valueWithBytes: "a" objCType: @encode(char)],
	    [OFValue valueWithBytes: "b" objCType: @encode(char)]);

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFWindowsRegistryKeyTests.m from [0f0fbee00b] to [295a442df9].

11
12
13
14
15
16
17
18


19








































20
21
22
23
24
25































26

27









28
29
30
31
32

33
34
35

36
37
38

39
40
41
42
43
44

45
46
47

48
49
50


51
52
53
54
55
56
57




58
59
60
61
62
63
64
65
66
67
68

69
70
71
72

73
74

75
76
77
78
79
80
81
82


83
84
85
86


87
88
89
90
91
92
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60






61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

94
95
96
97
98
99
100
101
102
103
104



105



106



107


108



109



110



111
112







113
114
115
116







117



118




119


120








121
122

123


124
125




126
127







-
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
+
+
+
+
+
+
+
+


-
-
-
+
-
-
-
+
-
-
-
+
-
-

-
-
-
+
-
-
-
+
-
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-

-
-
-
+
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
+
+
-

-
-
+
+
-
-
-
-


 * 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 "TestsAppDelegate.h"
#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],
static OFString *const module = @"OFWindowsRegistryKey";

@implementation TestsAppDelegate (OFWindowsRegistryKeyTests)
- (void)windowsRegistryKeyTests
{
	void *pool = objc_autoreleasePoolPush();
	    [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;
	OFWindowsRegistryKey *softwareKey, *objFWKey;

	[_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])

	[_objFWKey setString: @"foobar" forValueNamed: @"string"];
	TEST(@"+[OFWindowsRegistryKey currentConfigKey]",
	    [OFWindowsRegistryKey currentConfigKey])

	OTAssertEqualObjects([_objFWKey stringForValueNamed: @"string"],
	TEST(@"+[OFWindowsRegistryKey currentUserKey]",
	    [OFWindowsRegistryKey currentUserKey])

	    @"foobar");
	TEST(@"+[OFWindowsRegistryKey localMachineKey]",
	    [OFWindowsRegistryKey localMachineKey])

	TEST(@"+[OFWindowsRegistryKey usersKey]",
	    [OFWindowsRegistryKey usersKey])

	[_objFWKey setString: @"%PATH%;foo"
	    TEST(@"-[openSubkeyAtPath:accessRights:options:] #1",
	    (softwareKey = [[OFWindowsRegistryKey currentUserKey]
		openSubkeyAtPath: @"Software"
	       forValueNamed: @"expand"
		    accessRights: KEY_ALL_ACCESS
			 options: 0]))

			type: REG_EXPAND_SZ];
	OTAssertEqualObjects([_objFWKey stringForValueNamed: @"expand"
	EXPECT_EXCEPTION(@"-[openSubkeyAtPath:accessRights:options:] #2",
	    OFOpenWindowsRegistryKeyFailedException,
	    [[OFWindowsRegistryKey currentUserKey]
		openSubkeyAtPath: @"nonexistent"
		    accessRights: KEY_ALL_ACCESS
			 options: 0])

						       type: &type],
	    @"%PATH%;foo");
	OTAssertEqual(type, REG_EXPAND_SZ);
}
	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]))

- (void)testDeleteValue
	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"]) &&
	[_objFWKey setString: @"foobar" forValueNamed: @"deleteme"];
	    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"] &&
	OTAssertEqualObjects([_objFWKey stringForValueNamed: @"deleteme"],
	    @"foobar");
	    type == REG_EXPAND_SZ)

	TEST(@"-[deleteValueNamed:]", R([objFWKey deleteValueNamed: @"data"]))

	[_objFWKey deleteValueNamed: @"deleteme"];
	OTAssertNil([_objFWKey stringForValueNamed: @"deleteme"]);
	TEST(@"-[deleteSubkeyAtPath:]",
	    R([softwareKey deleteSubkeyAtPath: @"ObjFW"]))

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFXMLElementBuilderTests.m from [94889a4bd5] to [7e8aac9296].

11
12
13
14
15
16
17
18


19
20
21
22
23
24
















25
26
27
28
29


30
31
32
33
34
35
36


37
38
39

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55





56
57
58

59
60
61
62
11
12
13
14
15
16
17

18
19
20





21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39


40
41
42
43
44
45
46


47
48
49
50

51
52

53
54
55
56
57
58
59
60
61





62
63
64
65
66

67

68


69
70







-
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
+
+





-
-
+
+


-
+

-









-
-
-
-
-
+
+
+
+
+
-

-
+
-
-


 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFXMLElementBuilder";
static OFXMLNode *nodes[2];
static size_t i = 0;

@implementation TestsAppDelegate (OFXMLElementBuilderTests)
@interface OFXMLElementBuilderTests: OTTestCase <OFXMLElementBuilderDelegate>
{
	OFXMLNode *_nodes[2];
	size_t _i;
}
@end

@implementation OFXMLElementBuilderTests
- (void)dealloc
{
	[_nodes[0] release];
	[_nodes[1] release];

	[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 = @"<foo>bar<![CDATA[f<oo]]>baz<qux/>"
	    " <qux xmlns:qux='urn:qux'><?asd?><qux:bar/><x qux:y='z'/></qux>"
	    "</foo>";

	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: @"<!--foo-->"]) &&
	    nodes[1] != nil && [nodes[1].XMLString isEqual: @"<!--foo-->"] &&
	[parser parseString: string];
	OTAssertEqualObjects(_nodes[0].XMLString, string);

	[parser parseString: @"<!--foo-->"];
	OTAssertEqualObjects(_nodes[1].XMLString, @"<!--foo-->");
	    i == 2)

	[nodes[0] release];
	OTAssertEqual(_i, 2);
	[nodes[1] release];
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFXMLNodeTests.m from [806daf51ab] to [eb43bc25ed].

11
12
13
14
15
16
17
18


19
20


21
22
23


24
25
26
27
28




29
30
31

32
33
34

35
36
37
38
39





40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60

61
62
63
64

65
66
67
68
69
70
71






72
73
74


75
76
77


78
79
80
81



82
83
84


85
86

87
88
89
90
91
92
93
94






95
96
97
98
99
100
101
102
103
104
105

106
107
108
109
110
111

112
113

114
115
116
117
118
119
120
121
122



































































123



124
125
126
127
128


















































129





130
131
132
133






134
135
136
137
11
12
13
14
15
16
17

18
19
20

21
22
23


24
25
26




27
28
29
30

31

32



33





34
35
36
37
38












39


40






41




42



43



44
45
46
47
48
49
50


51
52



53
54




55
56
57



58
59


60








61
62
63
64
65
66







67



68






69


70
71








72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142





143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198




199
200
201
202
203
204


205
206







-
+
+

-
+
+

-
-
+
+

-
-
-
-
+
+
+
+
-

-
+
-
-
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-

-
-
+
-
-
-
-
-
-
+
-
-
-
-
+
-
-
-

-
-
-
+
+
+
+
+
+

-
-
+
+
-
-
-
+
+
-
-
-
-
+
+
+
-
-
-
+
+
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-

-
-
-
+
-
-
-
-
-
-
+
-
-
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-


 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *module;
@interface OFXMLNodeTests: OTTestCase
@end

@implementation TestsAppDelegate (OFXMLNodeTests)
- (void)XMLNodeTests
@implementation OFXMLNodeTests
- (void)testElementWithName
{
	void *pool = objc_autoreleasePoolPush();
	id node1, node2, node3, node4;
	OFArray *array;

	OTAssertEqualObjects(
	    [[OFXMLElement elementWithName: @"foo"] XMLString],
	    @"<foo/>");
}
	module = @"OFXMLNode";

	TEST(@"+[elementWithName:]",
- (void)testElementWithNameStringValue
	    (node1 = [OFXMLElement elementWithName: @"foo"]) &&
	    [[node1 XMLString] isEqual: @"<foo/>"])

{
	TEST(@"+[elementWithName:stringValue:]",
	    (node2 = [OFXMLElement elementWithName: @"foo"
				       stringValue: @"b&ar"]) &&
	    [[node2 XMLString] isEqual: @"<foo>b&amp;ar</foo>"])

	OTAssertEqualObjects(
	    [[OFXMLElement elementWithName: @"foo"
			       stringValue: @"b&ar"] XMLString],
	    @"<foo>b&amp;ar</foo>");
}
	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: @"<objfw-test:foo test='test'/>"] &&
	    (node4 = [OFXMLElement elementWithName: @"foo"
					 namespace: @"urn:objfw:test"]) &&
	    R([node4 addAttributeWithName: @"test" stringValue: @"test"]) &&
	    [[node4 XMLString] isEqual:
	    @"<foo xmlns='urn:objfw:test' test='test'/>"])

	TEST(@"+[elementWithName:namespace:stringValue:]",
	    (node4 = [OFXMLElement elementWithName: @"foo"
- (void)testElementWithNameNamespace
					 namespace: @"urn:objfw:test"
				       stringValue: @"x"]) &&
	    R([node4 setPrefix: @"objfw-test"
		  forNamespace: @"urn:objfw:test"]) &&
	    [[node4 XMLString] isEqual: @"<objfw-test:foo>x</objfw-test:foo>"])

{
	TEST(@"+[charactersWithString:]",
	    (node4 = [OFXMLCharacters charactersWithString: @"<foo>"]) &&
	    [[node4 XMLString] isEqual: @"&lt;foo&gt;"])

	OFXMLElement *element;
	TEST(@"+[CDATAWithString:]",
	    (node4 = [OFXMLCDATA CDATAWithString: @"<foo>"]) &&
	    [[node4 XMLString] isEqual: @"<![CDATA[<foo>]]>"]);

	TEST(@"+[commentWithText:]",
	    (node4 = [OFXMLComment commentWithText: @" comment "]) &&
	    [[node4 XMLString] isEqual: @"<!-- comment -->"])
	element = [OFXMLElement elementWithName: @"foo"
				      namespace: @"urn:objfw:test"];
	[element addAttributeWithName: @"test" stringValue: @"test"];
	[element setPrefix: @"objfw-test" forNamespace: @"urn:objfw:test"];
	OTAssertEqualObjects(element.XMLString,
	    @"<objfw-test:foo test='test'/>");

	module = @"OFXMLElement";

	element = [OFXMLElement elementWithName: @"foo"
				      namespace: @"urn:objfw:test"];
	TEST(@"-[addAttributeWithName:stringValue:]",
	    R([node1 addAttributeWithName: @"foo" stringValue: @"b&ar"]) &&
	    [[node1 XMLString] isEqual: @"<foo foo='b&amp;ar'/>"] &&
	[element addAttributeWithName: @"test" stringValue: @"test"];
	OTAssertEqualObjects(element.XMLString,
	    R([node2 addAttributeWithName: @"foo" stringValue: @"b&ar"]) &&
	    [[node2 XMLString] isEqual: @"<foo foo='b&amp;ar'>b&amp;ar</foo>"])

	TEST(@"-[setPrefix:forNamespace:]",
	    @"<foo xmlns='urn:objfw:test' test='test'/>");
}

	    R([node2 setPrefix: @"objfw-test"
		  forNamespace: @"urn:objfw:test"]))

- (void)testElementWithNameNamespaceStringValue
{
	TEST(@"-[addAttributeWithName:namespace:stringValue:]",
	    R([node2 addAttributeWithName: @"foo"
	OFXMLElement *element = [OFXMLElement elementWithName: @"foo"
				namespace: @"urn:objfw:test"
			      stringValue: @"bar"]) &&
	    R([node2 addAttributeWithName: @"foo"
				namespace: @"urn:objfw:test"
			      stringValue: @"ignored"]) &&
	    [[node2 XMLString] isEqual:
	    @"<foo foo='b&amp;ar' objfw-test:foo='bar'>b&amp;ar</foo>"])

						    namespace: @"urn:objfw:test"
						  stringValue: @"x"];
	[element setPrefix: @"objfw-test" forNamespace: @"urn:objfw:test"];
	OTAssertEqualObjects(element.XMLString,
	    @"<objfw-test:foo>x</objfw-test:foo>");
}
	TEST(@"-[removeAttributeForName:namespace:]",
	    R([node2 removeAttributeForName: @"foo"]) &&
	    [[node2 XMLString] isEqual:
	    @"<foo objfw-test:foo='bar'>b&amp;ar</foo>"] &&
	    R([node2 removeAttributeForName: @"foo"
				  namespace: @"urn:objfw:test"]) &&
	    [[node2 XMLString] isEqual: @"<foo>b&amp;ar</foo>"])

	TEST(@"-[addChild:]",
	    R([node1 addChild: [OFXMLElement elementWithName: @"bar"]]) &&
	    [[node1 XMLString] isEqual:
- (void)testElementWithXMLStringAndStringValue
	    @"<foo foo='b&amp;ar'><bar/></foo>"] &&
	    R([node3 addChild: [OFXMLElement elementWithName: @"bar"
		    namespace: @"urn:objfw:test"]]) &&
	    [[node3 XMLString] isEqual:
	    @"<objfw-test:foo test='test'><objfw-test:bar/></objfw-test:foo>"])

{
	TEST(@"+[elementWithXMLString:] and -[stringValue]",
	    [[[OFXMLElement elementWithXMLString:
	OTAssertEqualObjects([[OFXMLElement elementWithXMLString:
	    @"<?xml version='1.0' encoding='UTF-8'?>\r\n<x>foo<![CDATA[bar]]>"
	    @"<y>b<!-- fooo -->az</y>qux</x>"] stringValue]
	    isEqual: @"foobarbazqux"])

	TEST(@"-[elementsForName:namespace:]",
	    (array = [node3 elementsForName: @"bar"
				  namespace: @"urn:objfw:test"]) &&
	    array.count == 1 && [[array.firstObject XMLString] isEqual:
	    @"<bar xmlns='urn:objfw:test'/>"])
	    @"<y>b<!-- fooo -->az</y>qux</x>"] stringValue],
	    @"foobarbazqux");
}

- (void)testCharactersWithString
{
	OTAssertEqualObjects(
	    [[OFXMLCharacters charactersWithString: @"<foo>"] XMLString],
	    @"&lt;foo&gt;");
}

- (void)testCDATAWithString
{
	OTAssertEqualObjects(
	    [[OFXMLCDATA CDATAWithString: @"<foo>"] XMLString],
	    @"<![CDATA[<foo>]]>");
}

- (void)testCommentWithText
{
	OTAssertEqualObjects(
	    [[OFXMLComment commentWithText: @" comment "] XMLString],
	    @"<!-- comment -->");
}

- (void)testIsEqual
{
	OTAssertEqualObjects(
	    [OFXMLElement elementWithXMLString: @"<foo bar='asd'/>"],
	    [OFXMLElement elementWithXMLString: @"<foo bar='asd'></foo>"]);

	OTAssertEqualObjects(
	    [OFXMLElement elementWithXMLString: @"<x><y/></x>"],
	    [OFXMLElement elementWithXMLString: @"<x><y></y></x>"]);

	OTAssertNotEqualObjects(
	    [OFXMLElement elementWithXMLString: @"<x><Y/></x>"],
	    [OFXMLElement elementWithXMLString: @"<x><y></y></x>"]);
}

- (void)testHash
{
	OTAssertEqual(
	    [[OFXMLElement elementWithXMLString: @"<foo bar='asd'/>"] hash],
	    [[OFXMLElement elementWithXMLString: @"<foo bar='asd'></foo>"]
	    hash]);

	OTAssertEqual(
	    [[OFXMLElement elementWithXMLString: @"<x><y/></x>"] hash],
	    [[OFXMLElement elementWithXMLString: @"<x><y></y></x>"] hash]);

	OTAssertNotEqual(
	    [[OFXMLElement elementWithXMLString: @"<x><Y/></x>"] hash],
	    [[OFXMLElement elementWithXMLString: @"<x><y></y></x>"] 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,
	    @"<foo foo='b&amp;ar' objfw-test:foo='bar'>b&amp;ar</foo>");
}
	TEST(@"-[isEqual:]",
	    [[OFXMLElement elementWithXMLString: @"<foo bar='asd'/>"] isEqual:
	    [OFXMLElement elementWithXMLString: @"<foo bar='asd'></foo>"]] &&
	    [[OFXMLElement elementWithXMLString: @"<x><y/></x>"] isEqual:
	    [OFXMLElement elementWithXMLString: @"<x><y></y></x>"]])

- (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,
	    @"<foo objfw-test:foo='bar'>b&amp;ar</foo>");

	[element removeAttributeForName: @"foo" namespace: @"urn:objfw:test"];
	OTAssertEqualObjects(element.XMLString, @"<foo>b&amp;ar</foo>");
}

- (void)testAddChild
{
	OFXMLElement *element;

	element = [OFXMLElement elementWithName: @"foo"];
	[element addAttributeWithName: @"foo" stringValue: @"b&ar"];
	[element addChild: [OFXMLElement elementWithName: @"bar"]];
	OTAssertEqualObjects(element.XMLString,
	    @"<foo foo='b&amp;ar'><bar/></foo>");

	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,
	    @"<objfw-test:foo test='test'><objfw-test:bar/></objfw-test:foo>");
}

- (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]);
}

	TEST(@"-[XMLStringWithIndentation:]",
	    [[[OFXMLElement elementWithXMLString: @"<x><y><z>a\nb</z>"
	    @"<!-- foo --></y></x>"] XMLStringWithIndentation: 2] isEqual:
	    @"<x>\n  <y>\n    <z>a\nb</z>\n    <!-- foo -->\n  </y>\n</x>"])
- (void)testXMLStringWithIndentation
{
	OTAssertEqualObjects([[OFXMLElement
	    elementWithXMLString: @"<x><y><z>a\nb</z><!-- foo --></y></x>"]
	    XMLStringWithIndentation: 2],
	    @"<x>\n  <y>\n    <z>a\nb</z>\n    <!-- foo -->\n  </y>\n</x>");

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFXMLParserTests.m from [e9e91152cf] to [7002f67f2b].

14
15
16
17
18
19
20
21


22
23
24





25
26
27
28
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50


51
52
53
54



55
56

57
58
59



60
61

62
63
64





65
66

67
68


69
70

71
72
73










74
75
76
77
78





79
80
81
82

83

84
85


86
87






88
89
90
91

92
93

94
95
96
97
98
99
100
101






102
103



104
105


106
107
108
109
110
111








112
113
114
115
116




117
118

119
120


121
122

123
124
125
126
127
128






129
130
131
132
133



134
135
136

137
138
139
140
141
142
143
144
145



146
147
148
149
150
151

152
153
154
155
156
157
158
159
160

161
162

163
164






165
166








167
168
169
170
171


172
173
174
175

176
177
178
179

180
181
182
183
184
185
186
187
188
189
190
191
192
193


194
195
196


197
198










199
200
201
202
203



204
205
206
207
208
209
210

211
212
213
214
215
216
217
218
219
220
221
222
223
224


225
226
227
228




229
230
231
232
233


234
235
236
237
238


239
240
241
242


243
244
245
246
247
248
249
250
251
252




253
254

255
256


257
258

259
260
261
262




263
264

265
266


267
268

269
270
271




272
273
274
275
276
277
278
14
15
16
17
18
19
20

21
22
23


24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47







48
49




50
51
52
53

54



55
56
57
58

59



60
61
62
63
64
65

66


67
68
69

70



71
72
73
74
75
76
77
78
79
80
81
82



83
84
85
86
87
88
89


90

91
92
93
94
95


96
97
98
99
100
101
102
103


104


105






106
107
108
109
110
111
112
113


114
115
116
117
118
119
120






121
122
123
124
125
126
127
128





129
130
131
132
133

134


135
136
137

138






139
140
141
142
143
144





145
146
147



148




149
150



151
152
153






154





155
156


157


158
159
160
161
162
163
164
165
166


167
168
169
170
171
172
173
174
175
176



177
178




179




180









181
182



183
184

185
186
187
188


189
190
191
192
193
194
195
196
197
198
199
200



201
202
203







204









205
206



207
208
209
210


211
212
213
214
215
216



217
218

219
220


221
222
223
224


225
226
227
228








229
230
231
232
233

234


235
236
237

238




239
240
241
242
243

244


245
246
247

248



249
250
251
252
253
254
255
256
257
258
259







-
+
+

-
-
+
+
+
+
+










-
+








-
-
-
-
-
-
-
+
+
-
-
-
-
+
+
+

-
+
-
-
-
+
+
+

-
+
-
-
-
+
+
+
+
+

-
+
-
-
+
+

-
+
-
-
-
+
+
+
+
+
+
+
+
+
+


-
-
-
+
+
+
+
+


-
-
+
-
+


+
+
-
-
+
+
+
+
+
+


-
-
+
-
-
+
-
-
-
-
-
-


+
+
+
+
+
+
-
-
+
+
+


+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+

-
+
-
-
+
+

-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
-
-
-
+
-
-
-
-


-
-
-
+
+
+
-
-
-
-
-
-
+
-
-
-
-
-


-
-
+
-
-
+


+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+


-
-
-
+
+
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-


-
-
-
+
+
-


+
+
-
-
+
+
+
+
+
+
+
+
+
+


-
-
-
+
+
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-


-
-
-
+
+


-
-
+
+
+
+


-
-
-
+
+
-


-
-
+
+


-
-
+
+


-
-
-
-
-
-
-
-
+
+
+
+

-
+
-
-
+
+

-
+
-
-
-
-
+
+
+
+

-
+
-
-
+
+

-
+
-
-
-
+
+
+
+







 */

#include "config.h"

#include <stdlib.h>
#include <string.h>

#import "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"OFXMLParser";
static int i = 0;
@interface OFXMLParserTests: OTTestCase <OFXMLParserDelegate>
{
	int _i;
}
@end

enum EventType {
	eventTypeProcessingInstruction,
	eventTypeTagOpen,
	eventTypeTagClose,
	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) {
	case 1:
	switch (_i++) {
	case 0:
		TEST(message,
		    type == eventTypeProcessingInstruction &&
		    [name isEqual: @"xml"] &&
		    [string isEqual: @"version='1.0'"])
		OTAssertEqual(type, eventTypeProcessingInstruction);
		OTAssertEqualObjects(name, @"xml");
		OTAssertEqualObjects(string, @"version='1.0'");
		break;
	case 2:
	case 1:
		TEST(message,
		    type == eventTypeProcessingInstruction &&
		    [name isEqual: @"p?i"] && string == nil)
		OTAssertEqual(type, eventTypeProcessingInstruction);
		OTAssertEqualObjects(name, @"p?i");
		OTAssertNil(string);
		break;
	case 3:
	case 2:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"root"] &&
		    prefix == nil && namespace == nil && attrs.count == 0)
		OTAssertEqual(type, eventTypeTagOpen);
		OTAssertEqualObjects(name, @"root");
		OTAssertNil(prefix);
		OTAssertNil(namespace);
		OTAssertEqual(attrs.count, 0);
		break;
	case 4:
	case 3:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n\n "])
		OTAssertEqual(type, eventTypeString);
		OTAssertEqualObjects(string, @"\n\n ");
		break;
	case 5:
	case 4:
		TEST(message,
		    type == eventTypeCDATA && [string isEqual: @"f<]]]oo]"] &&
		    parser.lineNumber == 3)
		OTAssertEqual(type, eventTypeCDATA);
		OTAssertEqualObjects(string, @"f<]]]oo]");
		OTAssertEqual(parser.lineNumber, 3);
		break;
	case 5:
		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"] &&
		OTAssertEqual(type, eventTypeString);
		    prefix == nil && namespace == nil && attrs == nil)
		OTAssertEqualObjects(string, @"\n ");
		break;
	case 8:
		OTAssertEqual(type, eventTypeTagOpen);
		OTAssertEqualObjects(name, @"foobar");
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n "])
		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"] &&
		OTAssertEqual(type, eventTypeString);
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"] &&
		OTAssertEqualObjects(string, @"\n  ");
		    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"])
		break;
	case 10:
		OTAssertEqual(type, eventTypeTagOpen);
		OTAssertEqualObjects(name, @"qux");
		OTAssertNil(prefix);
		OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar");
		OTAssertEqualObjects(attrs, [OFArray arrayWithObject:
		    [OFXMLAttribute attributeWithName: @"foo"
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n  "])
					    namespace: @"http://www.w3.org/"
						       @"2000/xmlns/"
					  stringValue: @"urn:objfw:test:foo"]]);
		break;
	case 11:
		OTAssertEqual(type, eventTypeString);
		OTAssertEqualObjects(string, @"\n   ");
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"qux"] &&
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"] &&
		    attrs.count == 1 &&
		    /* xmlns:foo attr */
		break;
	case 12:
		OTAssertEqual(type, eventTypeTagOpen);
		OTAssertEqualObjects(name, @"bla");
		OTAssertEqualObjects(prefix, @"foo");
		OTAssertEqualObjects(namespace, @"urn:objfw:test:foo");
		OTAssertEqualObjects(attrs, ([OFArray arrayWithObjects:
		    [OFXMLAttribute attributeWithName: @"bla"
		    [[[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"])
					    namespace: @"urn:objfw:test:foo"
					  stringValue: @"bla"],
		    [OFXMLAttribute attributeWithName: @"blafoo"
					  stringValue: @"foo"], nil]));
		break;
	case 12:
	case 13:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n   "])
		OTAssertEqual(type, eventTypeString);
		OTAssertEqualObjects(string, @"\n    ");
		break;
	case 13:
	case 14:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"bla"] &&
		    [prefix isEqual: @"foo"] &&
		    [namespace isEqual: @"urn:objfw:test:foo"] &&
		    attrs.count == 2 &&
		    /* foo:bla attr */
		OTAssertEqual(type, eventTypeTagOpen);
		OTAssertEqualObjects(name, @"blup");
		OTAssertNil(prefix);
		OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar");
		OTAssertEqualObjects(attrs, ([OFArray arrayWithObjects:
		    [OFXMLAttribute attributeWithName: @"qux"
		    [[[attrs objectAtIndex: 0] name] isEqual: @"bla"] &&
		    [[[attrs objectAtIndex: 0] namespace] isEqual:
		    @"urn:objfw:test:foo"] &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual: @"bla"] &&
		    /* blafoo attr */
					    namespace: @"urn:objfw:test:foo"
					  stringValue: @"asd"],
		    [OFXMLAttribute attributeWithName: @"quxqux"
		    [[[attrs objectAtIndex: 1] name] isEqual: @"blafoo"] &&
		    [[attrs objectAtIndex: 1] namespace] == nil &&
		    [[[attrs objectAtIndex: 1] stringValue] isEqual: @"foo"])
					  stringValue: @"test"], nil]));
		break;
	case 14:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n    "])
		break;
	case 15:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"blup"] &&
		    prefix == nil &&
		OTAssertEqual(type, eventTypeTagClose);
		OTAssertEqualObjects(name, @"blup");
		OTAssertNil(prefix);
		    [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"] &&
		OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar");
		    [[[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"])
		break;
	case 16:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"blup"] &&
		OTAssertEqual(type, eventTypeString);
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"])
		OTAssertEqualObjects(string, @"\n    ");
		break;
	case 17:
		OTAssertEqual(type, eventTypeTagOpen);
		OTAssertEqualObjects(name, @"bla");
		OTAssertEqualObjects(prefix, @"bla");
		OTAssertEqualObjects(namespace, @"urn:objfw:test:bla");
		OTAssertEqualObjects(attrs, ([OFArray arrayWithObjects:
		    [OFXMLAttribute attributeWithName: @"bla"
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n    "])
					    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"] &&
		OTAssertEqual(type, eventTypeTagClose);
		OTAssertEqualObjects(name, @"bla");
		    [namespace isEqual: @"urn:objfw:test:bla"] &&
		    attrs.count == 3 &&
		    /* xmlns:bla attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"bla"] &&
		OTAssertEqualObjects(prefix, @"bla");
		    [[[attrs objectAtIndex: 0] namespace] isEqual:
		    @"http://www.w3.org/2000/xmlns/"] &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual:
		    @"urn:objfw:test:bla"] &&
		OTAssertEqualObjects(namespace, @"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"])
		break;
	case 19:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"bla"] &&
		    [prefix isEqual: @"bla"] &&
		OTAssertEqual(type, eventTypeString);
		OTAssertEqualObjects(string, @"\n    ");
		    [namespace isEqual: @"urn:objfw:test:bla"])
		break;
	case 20:
		OTAssertEqual(type, eventTypeTagOpen);
		OTAssertEqualObjects(name, @"abc");
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n    "])
		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 &&
		OTAssertEqual(type, eventTypeTagClose);
		OTAssertEqualObjects(name, @"abc");
		OTAssertNil(prefix);
		    [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"] &&
		OTAssertEqualObjects(namespace, @"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"])
		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"] &&
		OTAssertEqual(type, eventTypeString);
		OTAssertEqualObjects(string, @"\n   ");
		    [namespace isEqual: @"urn:objfw:test:foo"])
		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  "])
		break;
	case 28:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"qux"] &&
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"])
		OTAssertEqual(type, eventTypeTagClose);
		OTAssertEqualObjects(name, @"qux");
		OTAssertNil(prefix);
		OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar");
		break;
	case 29:
	case 28:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n "])
		OTAssertEqual(type, eventTypeString);
		OTAssertEqualObjects(string, @"\n ");
		break;
	case 30:
	case 29:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"foobar"] &&
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"])
		OTAssertEqual(type, eventTypeTagClose);
		OTAssertEqualObjects(name, @"foobar");
		OTAssertNil(prefix);
		OTAssertEqualObjects(namespace, @"urn:objfw:test:foobar");
		break;
	case 31:
	case 30:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n"])
		OTAssertEqual(type, eventTypeString);
		OTAssertEqualObjects(string, @"\n");
		break;
	case 32:
	case 31:
		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
  foundProcessingInstructionWithTarget: (OFString *)target
				  text: (OFString *)text
353
354
355
356
357
358
359
360

361
362
363

364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381

382
383

384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399


400
401

402
403
404


405
406
407


408
409
410
411
412
413










414
415
416
417


418
419
420



421
422


423
424
425
426



427
428
429



430
431
432
334
335
336
337
338
339
340

341
342


343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360

361


362
363
364
365
366
367
368
369
370
371
372
373
374
375
376


377
378


379
380


381
382
383


384
385
386





387
388
389
390
391
392
393
394
395
396
397
398


399
400

401
402
403
404
405


406
407




408
409
410



411
412
413

414
415







-
+

-
-
+

















-
+
-
-
+














-
-
+
+
-
-
+

-
-
+
+

-
-
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+


-
-
+
+
-


+
+
+
-
-
+
+
-
-
-
-
+
+
+
-
-
-
+
+
+
-


{
	if ([entity isEqual: @"foo"])
		return @"foobar";

	return nil;
}

- (void)XMLParserTests
- (void)testParser
{
	void *pool = objc_autoreleasePoolPush();
	const char *string = "\xEF\xBB\xBF<?xml version='1.0'?><?p?i?>"
	static const char *string = "\xEF\xBB\xBF<?xml version='1.0'?><?p?i?>"
	    "<!DOCTYPE foo><root>\r\r"
	    " <![CDATA[f<]]]oo]]]><bar/>\n"
	    " <foobar xmlns='urn:objfw:test:foobar'>\r\n"
	    "  <qux xmlns:foo='urn:objfw:test:foo'>\n"
	    "   <foo:bla foo:bla = '&#x62;&#x6c;&#x61;' blafoo='foo'>\n"
	    "    <blup foo:qux='asd' quxqux='test'/>\n"
	    "    <bla:bla\r\rxmlns:bla\r=\t\"urn:objfw:test:bla\" qux='qux'\r\n"
	    "     bla:foo='blafoo'/>\n"
	    "    <abc xmlns='urn:objfw:test:abc' abc='abc' foo:abc='abc'/>\n"
	    "   </foo:bla>\n"
	    "   <!-- commänt -->\n"
	    "  </qux>\n"
	    " </foobar>\n"
	    "</root>";
	OFXMLParser *parser;
	size_t j, length;

	TEST(@"+[parser]", (parser = [OFXMLParser parser]))
	parser = [OFXMLParser parser];

	TEST(@"-[setDelegate:]", (parser.delegate = self))
	parser.delegate = self;

	/* Simulate a stream where we only get chunks */
	length = strlen(string);

	for (j = 0; j < length; j+= 2) {
		if (parser.hasFinishedParsing)
			abort();

		if (j + 2 > length)
			[parser parseBuffer: string + j length: 1];
		else
			[parser parseBuffer: string + j length: 2];
	}

	TEST(@"Checking if everything was parsed",
	    i == 32 && parser.lineNumber == 18)
	OTAssertEqual(_i, 32);
	OTAssertEqual(parser.lineNumber, 18);

	TEST(@"-[hasFinishedParsing]", parser.hasFinishedParsing)
	OTAssertTrue(parser.hasFinishedParsing);

	TEST(@"Parsing whitespaces after the document",
	    R([parser parseString: @" \t\r\n "]))
	/* Parsing whitespaces after the document */
	[parser parseString: @" \t\r\n "];

	TEST(@"Parsing comments after the document",
	    R([parser parseString: @" \t<!-- foo -->\r<!--bar-->\n "]))
	/* Parsing comments after the document */
	[parser parseString: @" \t<!-- foo -->\r<!--bar-->\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: @"<!["])
	/* Detection of junk after the document */
	OTAssertThrowsSpecific([parser parseString: @"a"],
	    OFMalformedXMLException);
	OTAssertThrowsSpecific([parser parseString: @"<!["],
	    OFMalformedXMLException);
}

- (void)testDetectionOfInvalidXMLProcessingInstructions
{
	OFXMLParser *parser;

	parser = [OFXMLParser parser];
	EXPECT_EXCEPTION(@"Detection of invalid XML processing instruction #1",
	    OFMalformedXMLException,
	OTAssertThrowsSpecific([parser parseString: @"<?xml version='2.0'?>"],
	    OFMalformedXMLException);
	    [parser parseString: @"<?xml version='2.0'?>"])

	parser = [OFXMLParser parser];
	OTAssertThrowsSpecific([parser parseString: @"<x><?xml?></x>"],
	    OFMalformedXMLException);
}
	EXPECT_EXCEPTION(@"Detection of invalid XML processing instruction #2",
	    OFInvalidEncodingException,

- (void)testDetectionOfInvalidEncoding
	    [parser parseString: @"<?xml encoding='UTF-7'?>"])

	parser = [OFXMLParser parser];
	EXPECT_EXCEPTION(@"Detection of invalid XML processing instruction #3",
{
	OFXMLParser *parser = [OFXMLParser parser];

	    OFMalformedXMLException,
	    [parser parseString: @"<x><?xml?></x>"])

	OTAssertThrowsSpecific(
	    [parser parseString: @"<?xml encoding='UTF-7'?>"],
	    OFInvalidEncodingException);
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/RuntimeARCTests.m from [464cdbc085] to [685be12c90].

11
12
13
14
15
16
17
18


19
20


21
22

23
24
25



















26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
11
12
13
14
15
16
17

18
19
20

21
22
23

24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

























-
+
+

-
+
+

-
+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


















-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 * 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 "TestsAppDelegate.h"
#import "ObjFW.h"
#import "ObjFWTest.h"

static OFString *const module = @"Runtime (ARC)";
@interface RuntimeARCTests: OTTestCase
@end

@interface RuntimeARCTest: OFObject
@interface RuntimeARCTestClass: OFObject
@end

@implementation RuntimeARCTest
@implementation RuntimeARCTests
- (void)testExceptionsDuringInit
{
	OTAssertThrows((void)[[RuntimeARCTestClass alloc] init]);
}

- (void)testWeakReferences
{
	id object = [[OFObject alloc] init];
	__weak id weak = object;

	OTAssertEqual(weak, object);

	object = nil;
	OTAssertNil(weak);
}
@end

@implementation RuntimeARCTestClass
- (instancetype)init
{
	self = [super init];

#if defined(OF_WINDOWS) && defined(OF_AMD64)
	/*
	 * Clang has a bug on Windows where it creates an invalid call into
	 * objc_retainAutoreleasedReturnValue(). Work around it by not using an
	 * autoreleased exception.
	 */
	@throw [[OFException alloc] init];
#else
	@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

Modified tests/RuntimeTests.m from [99fca0e28e] to [6e0282335c].

11
12
13
14
15
16
17
18


19
20
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
36
37










38





































































































39
40
41
42
43
44
45
11
12
13
14
15
16
17

18
19
20

21
22





23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151







-
+
+

-


-
-
-
-
-
+










+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







 * 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 "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;
@property (retain) OFString *bar;

- (id)nilSuperTest;
@end

@interface RuntimeTests: OTTestCase
{
	RuntimeTestClass *_test;
}
@end

@interface OFObject (SuperTest)
- (id)superTest;
@end

@implementation RuntimeTest
@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
{
	[_foo release];
	[_bar release];
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
160
161
162
163
164
165
166
167












































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- (id)nilSuperTest
{
	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 version [7482d2d1ef].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297









































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <OFApplicationDelegate>
{
	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)
    <OFXMLParserDelegate, OFXMLElementBuilderDelegate>
- (void)XMLParserTests;
@end

Deleted tests/TestsAppDelegate.m version [08be9d3a13].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514


































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <stdlib.h>

#import "TestsAppDelegate.h"

#ifdef OF_IOS
# include <CoreFoundation/CoreFoundation.h>
#endif

#ifdef OF_PSP
# include <pspmoduleinfo.h>
# include <pspkernel.h>
# include <pspdebug.h>
# include <pspctrl.h>
PSP_MODULE_INFO("ObjFW Tests", 0, 0, 0);
#endif

#ifdef OF_WII
# define asm __asm__
# include <gccore.h>
# include <wiiuse/wpad.h>
# undef asm
#endif

#ifdef OF_NINTENDO_DS
# define asm __asm__
# include <nds.h>
# 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 <switch.h>
# 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

Modified tests/iOS.xcodeproj/project.pbxproj from [ac2dd7d645] to [a002968e40].

1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17









+







// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	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 */; };
		4BC7FD112013960600280496 /* testfile.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BC7FD0D2013960600280496 /* testfile.bin */; };
		4BC7FD132013960600280496 /* testfile.ini in Resources */ = {isa = PBXBuildFile; fileRef = 4BC7FD0F2013960600280496 /* testfile.ini */; };
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61


62
63
64
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83







+
















-

+
+











+







				4BC7FD0B2013956D00280496 /* ObjFW.framework in CopyFiles */,
			);
			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 = "<group>"; };
		4B6AB9CA202BA408007BAC7D /* TestPlugin.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = TestPlugin.bundle; path = plugin/TestPlugin.bundle; sourceTree = "<group>"; };
		4BC7FD06201394F300280496 /* ObjFW.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ObjFW.framework; path = ../src/ObjFW.framework; sourceTree = "<group>"; };
		4BC7FD082013954B00280496 /* tests.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = tests.a; sourceTree = "<group>"; };
		4BC7FD0C2013960600280496 /* testfile.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = testfile.txt; sourceTree = "<group>"; };
		4BC7FD0D2013960600280496 /* testfile.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = testfile.bin; sourceTree = "<group>"; };
		4BC7FD0F2013960600280496 /* testfile.ini */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = testfile.ini; sourceTree = "<group>"; };
		4BEBFB5B2013934E002E8710 /* tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tests.app; sourceTree = BUILT_PRODUCTS_DIR; };
		4BEBFB6C2013934E002E8710 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; };
		4BEBFB6D2013934E002E8710 /* ImportTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImportTest.m; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */

/* 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 */

/* Begin PBXGroup section */
		4BC7FD05201394F300280496 /* Frameworks */ = {
			isa = PBXGroup;
			children = (
				4B6AB9CA202BA408007BAC7D /* TestPlugin.bundle */,
				4BC7FD082013954B00280496 /* tests.a */,
				4B1E2CEE2B8294F200BB28B6 /* libobjfwtest.a */,
				4BC7FD06201394F300280496 /* ObjFW.framework */,
			);
			name = Frameworks;
			sourceTree = "<group>";
		};
		4BEBFB522013934E002E8710 = {
			isa = PBXGroup;
300
301
302
303
304
305
306

307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326

327
328
329
330
331
332
333
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339







+




















+







				DEVELOPMENT_TEAM = MXKNFCKFL6;
				FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../src";
				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";
			};
			name = Debug;
		};
		4BEBFB732013934E002E8710 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CODE_SIGN_STYLE = Automatic;
				DEVELOPMENT_TEAM = MXKNFCKFL6;
				FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../src";
				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";
			};
			name = Release;

Modified tests/testfile.ini from [70f0fff498] to [9e2ef2dc1a].

1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
18
19
20
21









-
+











[tests]
foo = bar
foobar=baz
;comment

[foobar]
;foobarcomment
qux=" asd"
"quxqux " = asd
quxquxqux="hello\"w”rld"
quxquxqux="hello\"rld"
qux2="a\f"

[types]
integer = 0x20
bool = true
float = 0.5
array1 = 1
array2 = 1
double = 0.25
array1 = 2
array2 = 2

Modified utils/objfw-new/Makefile from [b67c1400ab] to [8cc4a388ef].

1
2
3
4
5

6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
13





+







include ../../extra.mk

PROG = objfw-new${PROG_SUFFIX}
SRCS = NewApp.m		\
       NewClass.m	\
       NewTest.m	\
       ObjFWNew.m	\
       Property.m

include ../../buildsys.mk

${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2}

Added utils/objfw-new/NewTest.m version [0991f0679f].























































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
 *
 * 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 <errno.h>

#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 <ObjFW/ObjFW.h>\n"
			   @"#import <ObjFWTest/ObjFWTest.h>\n"
			   @"\n"
			   @"@interface %@: OTTestCase\n"
			   @"@end\n"
			   @"\n"
			   @"@implementation %@\n"
			   @"@end\n",
			   name, name];

	[file close];
}

Modified utils/objfw-new/ObjFWNew.m from [5f604351d2] to [0b89b26b9a].

23
24
25
26
27
28
29

30
31
32
33
34
35
36
37


38
39
40
41
42
43
44
45
46
47
48
49
50
51




52
53
54
55
56
57
58
59
60

61
62
63
64
65
66

67
68

69
70
71
72
73
74
75
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
48


49
50

51
52
53
54
55
56
57
58
59
60
61
62

63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79







+







-
+
+









-
-


-
+
+
+
+








-
+






+

-
+







#import "OFString.h"

@interface ObjFWNew: OFObject <OFApplicationDelegate>
@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 },
		{ 'p', @"property", 1, NULL, NULL },
		{ 's', @"superclass", 1, NULL, &superclass },
		{ 'p', @"property", 1, NULL, NULL },
		{ 't', @"test", 0, &test, NULL },
		{ '\0', nil, 0, NULL, NULL }
	};
	OFOptionsParser *optionsParser;
	OFUnichar option;

	if ([OFApplication arguments].count == 0)
		help(OFStdErr, true, 1);
90
91
92
93
94
95
96

97

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112


113
114
115
116
117
118
94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125







+
-
+















+
+






		case ':':
		case '=':
			help(OFStdErr, false, 1);
			break;
		}
	}

	if (app + class + test != 1 ||
	if ((app ^ class) != 1 || optionsParser.remainingArguments.count != 1)
	    optionsParser.remainingArguments.count != 1)
		help(OFStdErr, false, 1);

	if ((superclass && !class)  || (properties != nil && !class))
		help(OFStdErr, false, 1);

	name = optionsParser.remainingArguments.firstObject;
	if ([name rangeOfString: @"."].location != OFNotFound) {
		[OFStdErr writeLine: @"Name must not contain dots!"];
		[OFApplication terminate];
	}

	if (app)
		newApp(name);
	else if (class)
		newClass(name, superclass, properties);
	else if (test)
		newTest(name);
	else
		help(OFStdErr, false, 1);

	[OFApplication terminate];
}
@end