@@ -13,77 +13,262 @@ * file. */ #include "config.h" -#import "TestsAppDelegate.h" +#import "OFDictionaryTests.h" -static OFString *module; static OFString *keys[] = { @"key1", @"key2" }; -static OFString *values[] = { +static OFString *objects[] = { @"value1", @"value2" }; -@interface SimpleDictionary: OFDictionary -{ - OFMutableDictionary *_dictionary; -} -@end - -@interface SimpleMutableDictionary: OFMutableDictionary -{ - OFMutableDictionary *_dictionary; - unsigned long _mutations; -} -@end - -@implementation SimpleDictionary -- (instancetype)init -{ - self = [super init]; - - @try { - _dictionary = [[OFMutableDictionary alloc] init]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (instancetype)initWithKey: (id)key arguments: (va_list)arguments -{ - self = [super init]; - - @try { - _dictionary = [[OFMutableDictionary alloc] - initWithKey: key - arguments: arguments]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (instancetype)initWithObjects: (const id *)objects +@interface CustomDictionary: OFDictionary +{ + OFDictionary *_dictionary; +} +@end + +@implementation OFDictionaryTests +- (Class)dictionaryClass +{ + return [CustomDictionary class]; +} + +- (void)setUp +{ + [super setUp]; + + _dictionary = [[self.dictionaryClass alloc] initWithObjects: objects + forKeys: keys + count: 2]; +} + +- (void)dealloc +{ + [_dictionary release]; + + [super dealloc]; +} + +- (void)testObjectForKey +{ + OTAssertEqualObjects([_dictionary objectForKey: keys[0]], objects[0]); + OTAssertEqualObjects([_dictionary objectForKey: keys[1]], objects[1]); +} + +- (void)testCount +{ + OTAssertEqual(_dictionary.count, 2); +} + +- (void)testIsEqual +{ + OTAssertEqualObjects(_dictionary, + [OFDictionary dictionaryWithObjects: objects + forKeys: keys + count: 2]); + OTAssertNotEqualObjects(_dictionary, + [OFDictionary dictionaryWithObjects: keys + forKeys: objects + count: 2]); +} + +/* FIXME: Hash is currently not stable. Enable test as soon as this is fixed. */ +#if 0 +- (void)testHash +{ + OTAssertEqual(_dictionary.hash, + [[OFDictionary dictionaryWithObjects: objects + forKeys: keys + count: 2] hash]); + OTAssertNotEqual(_dictionary.hash, + [[OFDictionary dictionaryWithObjects: keys + forKeys: objects + count: 2] hash]); +} +#endif + +- (void)testCopy +{ + OTAssertEqualObjects([[_dictionary copy] autorelease], _dictionary); +} + +- (void)testValueForKey +{ + OTAssertEqualObjects([_dictionary valueForKey: keys[0]], objects[0]); + OTAssertEqualObjects([_dictionary valueForKey: keys[1]], objects[1]); + OTAssertEqualObjects( + [_dictionary valueForKey: @"@count"], [OFNumber numberWithInt: 2]); +} + +- (void)testSetValueForKey +{ + OTAssertThrowsSpecific([_dictionary setValue: @"x" forKey: @"x"], + OFUndefinedKeyException); +} + +- (void)testContainsObject +{ + OTAssertTrue([_dictionary containsObject: objects[0]]); + OTAssertFalse([_dictionary containsObject: @"nonexistent"]); +} + +- (void)testContainsObjectIdenticalTo +{ + OTAssertTrue([_dictionary containsObjectIdenticalTo: objects[0]]); + OTAssertFalse([_dictionary containsObjectIdenticalTo: + [[objects[0] mutableCopy] autorelease]]); +} + +- (void)testDescription +{ + OTAssert( + [_dictionary.description isEqual: + @"{\n\tkey1 = value1;\n\tkey2 = value2;\n}"] || + [_dictionary.description isEqual: + @"{\n\tkey2 = value2;\n\tkey1 = value1;\n}"]); +} + +- (void)testAllKeys +{ + OTAssert( + [_dictionary.allKeys isEqual: + ([OFArray arrayWithObjects: keys[0], keys[1], nil])] || + [_dictionary.allKeys isEqual: + ([OFArray arrayWithObjects: keys[1], keys[0], nil])]); +} + +- (void)testAllObjects +{ + OTAssert( + [_dictionary.allObjects isEqual: + ([OFArray arrayWithObjects: objects[0], objects[1], nil])] || + [_dictionary.allObjects isEqual: + ([OFArray arrayWithObjects: objects[1], objects[0], nil])]); +} + +- (void)testKeyEnumerator +{ + OFEnumerator *enumerator = [_dictionary keyEnumerator]; + OFString *first, *second; + + first = [enumerator nextObject]; + second = [enumerator nextObject]; + OTAssertNil([enumerator nextObject]); + + OTAssert( + ([first isEqual: keys[0]] && [second isEqual: keys[1]]) || + ([first isEqual: keys[1]] && [second isEqual: keys[0]])); +} + +- (void)testObjectEnumerator +{ + OFEnumerator *enumerator = [_dictionary objectEnumerator]; + OFString *first, *second; + + first = [enumerator nextObject]; + second = [enumerator nextObject]; + OTAssertNil([enumerator nextObject]); + + OTAssert( + ([first isEqual: objects[0]] && [second isEqual: objects[1]]) || + ([first isEqual: objects[1]] && [second isEqual: objects[0]])); +} + +- (void)testFastEnumeration +{ + size_t i = 0; + OFString *first, *second; + + 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, *second; + + [_dictionary enumerateKeysAndObjectsUsingBlock: + ^ (id key, id object, bool *stop) { + OTAssertLessThan(i, 2); + + switch (i++) { + case 0: + first = key; + break; + case 1: + second = key; + break; + } + }]; + + OTAssertEqual(i, 2); + OTAssert( + ([first isEqual: keys[0]] && [second isEqual: keys[1]]) || + ([first isEqual: keys[1]] && [second isEqual: keys[0]])); +} + +- (void)testMappedDictionaryUsingBlock +{ + OTAssertEqualObjects([_dictionary mappedDictionaryUsingBlock: + ^ id (id key, id object) { + if ([key isEqual: keys[0]]) + return @"val1"; + if ([key isEqual: keys[1]]) + return @"val2"; + + return nil; + }], + ([OFDictionary dictionaryWithKeysAndObjects: + @"key1", @"val1", @"key2", @"val2", nil])); +} + +- (void)testFilteredDictionaryUsingBlock +{ + OTAssertEqualObjects([_dictionary filteredDictionaryUsingBlock: + ^ bool (id key, id object) { + return [key isEqual: keys[0]]; + }], + [OFDictionary dictionaryWithObject: objects[0] + forKey: keys[0]]); +} +#endif +@end + +@implementation CustomDictionary +- (instancetype)initWithObjects: (const id *)objects_ forKeys: (const id *)keys_ count: (size_t)count { self = [super init]; @try { - _dictionary = [[OFMutableDictionary alloc] - initWithObjects: objects - forKeys: keys_ - count: count]; + _dictionary = [[OFDictionary alloc] initWithObjects: objects_ + forKeys: keys_ + count: count]; } @catch (id e) { [self release]; @throw e; } @@ -109,272 +294,6 @@ - (OFEnumerator *)keyEnumerator { return [_dictionary keyEnumerator]; } -@end - -@implementation SimpleMutableDictionary -+ (void)initialize -{ - if (self == [SimpleMutableDictionary class]) - [self inheritMethodsFromClass: [SimpleDictionary class]]; -} - -- (void)setObject: (id)object forKey: (id)key -{ - bool existed = ([_dictionary objectForKey: key] == nil); - - [_dictionary setObject: object forKey: key]; - - if (existed) - _mutations++; -} - -- (void)removeObjectForKey: (id)key -{ - bool existed = ([_dictionary objectForKey: key] == nil); - - [_dictionary removeObjectForKey: key]; - - if (existed) - _mutations++; -} - -- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state - objects: (id *)objects - count: (int)count -{ - int ret = [super countByEnumeratingWithState: state - objects: objects - count: count]; - - state->mutationsPtr = &_mutations; - - return ret; -} -@end - -@implementation TestsAppDelegate (OFDictionaryTests) -- (void)dictionaryTestsWithClass: (Class)dictionaryClass - mutableClass: (Class)mutableDictionaryClass -{ - void *pool = objc_autoreleasePoolPush(); - OFMutableDictionary *mutableDict = [mutableDictionaryClass dictionary]; - OFDictionary *dict; - OFEnumerator *keyEnumerator, *objectEnumerator; - OFArray *keysArray, *valuesArray; - - [mutableDict setObject: values[0] forKey: keys[0]]; - [mutableDict setValue: values[1] forKey: keys[1]]; - - TEST(@"-[objectForKey:]", - [[mutableDict objectForKey: keys[0]] isEqual: values[0]] && - [[mutableDict objectForKey: keys[1]] isEqual: values[1]] && - [mutableDict objectForKey: @"key3"] == nil) - - TEST(@"-[valueForKey:]", - [[mutableDict valueForKey: keys[0]] isEqual: values[0]] && - [[mutableDict valueForKey: @"@count"] isEqual: - [OFNumber numberWithInt: 2]]) - - EXPECT_EXCEPTION(@"Catching -[setValue:forKey:] on immutable " - @"dictionary", OFUndefinedKeyException, - [[dictionaryClass dictionary] setValue: @"x" forKey: @"x"]) - - TEST(@"-[containsObject:]", - [mutableDict containsObject: values[0]] && - ![mutableDict containsObject: @"nonexistent"]) - - TEST(@"-[containsObjectIdenticalTo:]", - [mutableDict containsObjectIdenticalTo: values[0]] && - ![mutableDict containsObjectIdenticalTo: - [OFString stringWithString: values[0]]]) - - TEST(@"-[description]", - [[mutableDict description] isEqual: - @"{\n\tkey1 = value1;\n\tkey2 = value2;\n}"]) - - TEST(@"-[allKeys]", - [[mutableDict allKeys] isEqual: - [OFArray arrayWithObjects: keys[0], keys[1], nil]]) - - TEST(@"-[allObjects]", - [[mutableDict allObjects] isEqual: - [OFArray arrayWithObjects: values[0], values[1], nil]]) - - TEST(@"-[keyEnumerator]", (keyEnumerator = [mutableDict keyEnumerator])) - TEST(@"-[objectEnumerator]", - (objectEnumerator = [mutableDict objectEnumerator])) - - TEST(@"OFEnumerator's -[nextObject]", - [[keyEnumerator nextObject] isEqual: keys[0]] && - [[objectEnumerator nextObject] isEqual: values[0]] && - [[keyEnumerator nextObject] isEqual: keys[1]] && - [[objectEnumerator nextObject] isEqual: values[1]] && - [keyEnumerator nextObject] == nil && - [objectEnumerator nextObject] == nil) - - [mutableDict removeObjectForKey: keys[0]]; - - EXPECT_EXCEPTION(@"Detection of mutation during enumeration", - OFEnumerationMutationException, [keyEnumerator nextObject]); - - [mutableDict setObject: values[0] forKey: keys[0]]; - - size_t i = 0; - bool ok = true; - - for (OFString *key in mutableDict) { - if (i > 1 || ![key isEqual: keys[i]]) { - ok = false; - break; - } - - [mutableDict setObject: [mutableDict objectForKey: key] - forKey: key]; - i++; - } - - TEST(@"Fast Enumeration", ok) - - ok = false; - @try { - for (OFString *key in mutableDict) { - (void)key; - [mutableDict setObject: @"" forKey: @""]; - } - } @catch (OFEnumerationMutationException *e) { - ok = true; - } - - TEST(@"Detection of mutation during Fast Enumeration", ok) - - [mutableDict removeObjectForKey: @""]; - -#ifdef OF_HAVE_BLOCKS - { - __block size_t j = 0; - __block bool blockOk = true; - - [mutableDict enumerateKeysAndObjectsUsingBlock: - ^ (id key, id object, bool *stop) { - if (j > 1 || ![key isEqual: keys[j]]) { - blockOk = false; - *stop = true; - return; - } - - [mutableDict setObject: [mutableDict objectForKey: key] - forKey: key]; - j++; - }]; - - TEST(@"Enumeration using blocks", blockOk) - - blockOk = false; - @try { - [mutableDict enumerateKeysAndObjectsUsingBlock: - ^ (id key, id object, bool *stop) { - [mutableDict setObject: @"" forKey: @""]; - }]; - } @catch (OFEnumerationMutationException *e) { - blockOk = true; - } - - TEST(@"Detection of mutation during enumeration using blocks", - blockOk) - - [mutableDict removeObjectForKey: @""]; - } - - TEST(@"-[replaceObjectsUsingBlock:]", - R([mutableDict replaceObjectsUsingBlock: ^ id (id key, id object) { - if ([key isEqual: keys[0]]) - return @"value_1"; - if ([key isEqual: keys[1]]) - return @"value_2"; - - return nil; - }]) && [[mutableDict objectForKey: keys[0]] isEqual: @"value_1"] && - [[mutableDict objectForKey: keys[1]] isEqual: @"value_2"]) - - TEST(@"-[mappedDictionaryUsingBlock:]", - [[[mutableDict mappedDictionaryUsingBlock: - ^ id (id key, id object) { - if ([key isEqual: keys[0]]) - return @"val1"; - if ([key isEqual: keys[1]]) - return @"val2"; - - return nil; - }] description] isEqual: @"{\n\tkey1 = val1;\n\tkey2 = val2;\n}"]) - - TEST(@"-[filteredDictionaryUsingBlock:]", - [[[mutableDict filteredDictionaryUsingBlock: - ^ bool (id key, id object) { - return [key isEqual: keys[0]]; - }] description] isEqual: @"{\n\tkey1 = value_1;\n}"]) -#endif - - TEST(@"-[count]", mutableDict.count == 2) - - TEST(@"+[dictionaryWithKeysAndObjects:]", - (dict = [dictionaryClass dictionaryWithKeysAndObjects: - @"foo", @"bar", @"baz", @"qux", nil]) && - [[dict objectForKey: @"foo"] isEqual: @"bar"] && - [[dict objectForKey: @"baz"] isEqual: @"qux"]) - - TEST(@"+[dictionaryWithObject:forKey:]", - (dict = [dictionaryClass dictionaryWithObject: @"bar" - forKey: @"foo"]) && - [[dict objectForKey: @"foo"] isEqual: @"bar"]) - - keysArray = [OFArray arrayWithObjects: keys[0], keys[1], nil]; - valuesArray = [OFArray arrayWithObjects: values[0], values[1], nil]; - TEST(@"+[dictionaryWithObjects:forKeys:]", - (dict = [dictionaryClass dictionaryWithObjects: valuesArray - forKeys: keysArray]) && - [[dict objectForKey: keys[0]] isEqual: values[0]] && - [[dict objectForKey: keys[1]] isEqual: values[1]]) - - TEST(@"-[copy]", - (dict = [[dict copy] autorelease]) && - [[dict objectForKey: keys[0]] isEqual: values[0]] && - [[dict objectForKey: keys[1]] isEqual: values[1]]) - - TEST(@"-[mutableCopy]", - (mutableDict = [[dict mutableCopy] autorelease]) && - mutableDict.count == dict.count && - [[mutableDict objectForKey: keys[0]] isEqual: values[0]] && - [[mutableDict objectForKey: keys[1]] isEqual: values[1]] && - R([mutableDict setObject: @"value3" forKey: @"key3"]) && - [[mutableDict objectForKey: @"key3"] isEqual: @"value3"] && - [[mutableDict objectForKey: keys[0]] isEqual: values[0]] && - R([mutableDict setObject: @"foo" forKey: keys[0]]) && - [[mutableDict objectForKey: keys[0]] isEqual: @"foo"]) - - TEST(@"-[removeObjectForKey:]", - R([mutableDict removeObjectForKey: keys[0]]) && - [mutableDict objectForKey: keys[0]] == nil) - - [mutableDict setObject: @"foo" forKey: keys[0]]; - TEST(@"-[isEqual:]", ![mutableDict isEqual: dict] && - R([mutableDict removeObjectForKey: @"key3"]) && - ![mutableDict isEqual: dict] && - R([mutableDict setObject: values[0] forKey: keys[0]]) && - [mutableDict isEqual: dict]) - - objc_autoreleasePoolPop(pool); -} - -- (void)dictionaryTests -{ - module = @"OFDictionary"; - [self dictionaryTestsWithClass: [SimpleDictionary class] - mutableClass: [SimpleMutableDictionary class]]; - - module = @"OFDictionary_hashtable"; - [self dictionaryTestsWithClass: [OFDictionary class] - mutableClass: [OFMutableDictionary class]]; -} @end