/*
* Copyright (c) 2008-2021 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 "TestsAppDelegate.h"
#import "OFSet.h"
#import "OFMapTableSet.h"
#import "OFMutableMapTableSet.h"
static OFString *module;
@interface SimpleSet: OFSet
{
OFMutableSet *_set;
}
@end
@interface SimpleMutableSet: OFMutableSet
{
OFMutableSet *_set;
unsigned long _mutations;
}
@end
@implementation SimpleSet
- (instancetype)init
{
self = [super init];
@try {
_set = [[OFMutableSet alloc] init];
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
- (instancetype)initWithSet: (OFSet *)set
{
self = [super init];
@try {
_set = [[OFMutableSet alloc] initWithSet: set];
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
- (instancetype)initWithArray: (OFArray *)array
{
self = [super init];
@try {
_set = [[OFMutableSet alloc] initWithArray: array];
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments
{
self = [super init];
@try {
_set = [[OFMutableSet alloc] initWithObject: firstObject
arguments: arguments];
} @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];
}
@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 = @"OFMapTableSet";
[self setTestsWithClass: [OFMapTableSet class]
mutableClass: [OFMutableMapTableSet class]];
}
@end