-
Notifications
You must be signed in to change notification settings - Fork 283
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NSKVOSupport: Unit tests for the legacy API
- Loading branch information
Showing
1 changed file
with
315 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,315 @@ | ||
#import <GNUstepBase/GNUstep.h> | ||
|
||
#import <Foundation/NSObject.h> | ||
#import <Foundation/NSString.h> | ||
#import <Foundation/NSDictionary.h> | ||
#import <Foundation/NSSet.h> | ||
#import <Foundation/NSAutoreleasePool.h> | ||
#import <Foundation/NSKeyValueObserving.h> | ||
|
||
#import "Testing.h" | ||
|
||
@interface Observee : NSObject | ||
{ | ||
NSString *_firstName; | ||
NSString *_middleName; | ||
NSString *_lastName; | ||
} | ||
|
||
- (NSString *) firstName; | ||
- (void) setFirstName: (NSString *)name; | ||
|
||
- (NSString *) middleName; | ||
- (void) setMiddleName: (NSString *)name; | ||
|
||
- (NSString *) lastName; | ||
- (void) setLastName: (NSString *)name; | ||
|
||
- (NSString *) shortFullName; | ||
- (NSString *) fullName; | ||
|
||
@end | ||
|
||
@interface ObserveeMixed : Observee | ||
{ | ||
BOOL _trigger; | ||
} | ||
|
||
- (BOOL) trigger; | ||
- (void) setTrigger: (BOOL) trigger; | ||
|
||
@end | ||
|
||
@implementation Observee | ||
|
||
- (instancetype)init { | ||
self = [super init]; | ||
_firstName = _middleName = _lastName = @""; | ||
return self; | ||
} | ||
|
||
- (NSString *) firstName | ||
{ | ||
return _firstName; | ||
} | ||
- (void) setFirstName: (NSString *)name | ||
{ | ||
ASSIGN(_firstName, name); | ||
} | ||
- (NSString *) middleName | ||
{ | ||
return _middleName; | ||
} | ||
- (void) setMiddleName: (NSString *)name | ||
{ | ||
ASSIGN(_middleName, name); | ||
} | ||
- (NSString *) lastName | ||
{ | ||
return _lastName; | ||
} | ||
- (void) setLastName: (NSString *)name | ||
{ | ||
ASSIGN(_lastName, name); | ||
} | ||
|
||
- (NSString *) shortFullName { | ||
return [NSString stringWithFormat: @"%@ %@", _firstName, _lastName]; | ||
} | ||
|
||
- (NSString *) fullName { | ||
return [NSString stringWithFormat: @"%@ %@ %@", _firstName, _middleName, _lastName]; | ||
} | ||
|
||
@end | ||
|
||
@implementation ObserveeMixed | ||
|
||
- (BOOL) trigger | ||
{ | ||
return _trigger; | ||
} | ||
- (void) setTrigger: (BOOL) trigger | ||
{ | ||
_trigger = trigger; | ||
} | ||
|
||
// We expect this function to have priority over the legacy API | ||
+ (NSSet *)keyPathsForValuesAffectingFullName | ||
{ | ||
return [NSSet setWithObject:@"trigger"]; | ||
} | ||
|
||
@end | ||
|
||
@interface Observer : NSObject | ||
@end | ||
|
||
@implementation Observer | ||
{ | ||
@public | ||
NSString *lastKeyPath; | ||
id lastObject; | ||
NSDictionary *lastChange; | ||
int notificationCount; | ||
} | ||
|
||
- (void) resetValues | ||
{ | ||
lastKeyPath = nil; | ||
lastObject = nil; | ||
lastChange = nil; | ||
} | ||
|
||
- (void) resetCounter | ||
{ | ||
notificationCount = 0; | ||
} | ||
|
||
- (void)observeValueForKeyPath:(NSString *)keyPath | ||
ofObject:(id)object | ||
change:(NSDictionary *)change | ||
context:(void *)context | ||
{ | ||
notificationCount += 1; | ||
lastKeyPath = keyPath; | ||
lastObject = object; | ||
lastChange = change; | ||
} | ||
|
||
@end | ||
|
||
void simpleDependency(void) | ||
{ | ||
Observer *o = [Observer new]; | ||
Observee *e = [Observee new]; | ||
|
||
NSArray *keys = [NSArray arrayWithObjects: @"firstName", @"lastName", nil]; | ||
|
||
[Observee setKeys: keys | ||
triggerChangeNotificationsForDependentKey:@"fullName"]; | ||
|
||
NSSet *s = [Observee keyPathsForValuesAffectingValueForKey:@"fullName"]; | ||
NSSet *expectedSet = [NSSet setWithArray: keys]; | ||
PASS_EQUAL(s, expectedSet, "'keyPathsForValuesAffectingValueForKey:' returns the correct values affecting 'fullName'"); | ||
|
||
NSKeyValueObservingOptions opts = | ||
NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew; | ||
|
||
[e addObserver:o forKeyPath:@"fullName" options:opts context:NULL]; | ||
|
||
[e setFirstName:@"Hey"]; | ||
|
||
PASS_EQUAL(o->lastKeyPath, @"fullName", "last keypath is 'fullName'"); | ||
PASS_EQUAL(o->lastObject, e, "last change object is correct"); | ||
PASS(o->lastChange != nil, "last change is not nil"); | ||
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hey ", "new entry in change dict correct"); | ||
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @" ", "old entry in change dict correct"); | ||
PASS(o->notificationCount == 1, "notification count is 1"); | ||
|
||
[e setMiddleName:@"Not"]; // no change notification | ||
|
||
[e setLastName:@"You"]; | ||
PASS_EQUAL(o->lastKeyPath, @"fullName", "last keypath is 'fullName'"); | ||
PASS_EQUAL(o->lastObject, e, "last change object is correct"); | ||
PASS(o->lastChange != nil, "last change is not nil"); | ||
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hey Not You", "new entry in change dict correct"); | ||
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @"Hey Not ", "old entry in change dict correct"); | ||
PASS(o->notificationCount == 2, "notification count is 2"); | ||
|
||
[e removeObserver:o forKeyPath:@"fullName"]; | ||
|
||
[o release]; | ||
[e release]; | ||
} | ||
|
||
void registeringMultipleDependencies(void) { | ||
Observer *o; | ||
Observee *e; | ||
NSArray *arr; | ||
NSSet *s; | ||
|
||
o = [Observer new]; | ||
e = [Observee new]; | ||
|
||
arr = [NSArray arrayWithObject: @"firstName"]; | ||
[Observee setKeys:arr | ||
triggerChangeNotificationsForDependentKey:@"fullName"]; | ||
s = [Observee keyPathsForValuesAffectingValueForKey:@"fullName"]; | ||
PASS_EQUAL(s, [NSSet setWithArray: arr], "expecting 'firstName' as affecting key for 'fullName'"); | ||
|
||
arr = [NSArray arrayWithObject: @"middleName"]; | ||
[Observee setKeys: arr | ||
triggerChangeNotificationsForDependentKey:@"fullName"]; | ||
s = [Observee keyPathsForValuesAffectingValueForKey:@"fullName"]; | ||
PASS_EQUAL(s, [NSSet setWithArray: arr], "expecting 'middleName' as affecting key for 'fullName'"); | ||
|
||
arr = [NSArray arrayWithObjects: @"firstName", @"lastName", nil]; | ||
[Observee setKeys:arr | ||
triggerChangeNotificationsForDependentKey:@"shortFullName"]; | ||
s = [Observee keyPathsForValuesAffectingValueForKey:@"shortFullName"]; | ||
PASS_EQUAL(s, [NSSet setWithArray: arr], "expecting 'firstName' and 'lastName' as affecting key for 'fullName'"); | ||
|
||
NSKeyValueObservingOptions opts = | ||
NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew; | ||
|
||
[e addObserver:o forKeyPath:@"fullName" options:opts context:NULL]; | ||
|
||
[e setFirstName:@"Hey"]; | ||
PASS(o->notificationCount == 0, "no change notification received when modifying firstName"); | ||
|
||
[e setMiddleName:@"Not"]; | ||
PASS_EQUAL(o->lastKeyPath, @"fullName", "last keypath is 'fullName'"); | ||
PASS_EQUAL(o->lastObject, e, "last change object is correct"); | ||
PASS(o->lastChange != nil, "last change is not nil"); | ||
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hey Not ", "new entry in change dict correct"); | ||
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @"Hey ", "old entry in change dict correct"); | ||
PASS(o->notificationCount == 1, "change notification received when modifying middleName"); | ||
|
||
[e setLastName:@"You"]; | ||
PASS(o->notificationCount == 1, "no change notification received when modifying lastName"); | ||
|
||
[e removeObserver:o forKeyPath:@"fullName"]; | ||
[o resetCounter]; | ||
[o resetValues]; | ||
|
||
[e addObserver:o forKeyPath:@"shortFullName" options:opts context:NULL]; | ||
|
||
[e setFirstName:@"Hello"]; | ||
PASS_EQUAL(o->lastKeyPath, @"shortFullName", "last keypath is 'shortFullName'"); | ||
PASS_EQUAL(o->lastObject, e, "last change object is correct"); | ||
PASS(o->lastChange != nil, "last change is not nil"); | ||
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hello You", "new entry in change dict correct"); | ||
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @"Hey You", "old entry in change dict correct"); | ||
PASS(o->notificationCount == 1, "change notification received when modifying firstName"); | ||
|
||
[e setMiddleName:@"Not"]; | ||
PASS(o->notificationCount == 1, "no change notification received when modifying middleName"); | ||
|
||
[o resetValues]; | ||
|
||
[e setLastName:@"World"]; | ||
PASS_EQUAL(o->lastKeyPath, @"shortFullName", "last keypath is 'shortFullName'"); | ||
PASS_EQUAL(o->lastObject, e, "last change object is correct"); | ||
PASS(o->lastChange != nil, "last change is not nil"); | ||
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hello World", "new entry in change dict correct"); | ||
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @"Hello You", "old entry in change dict correct"); | ||
PASS(o->notificationCount == 2, "change notification received when modifying lastName"); | ||
|
||
[e removeObserver:o forKeyPath:@"shortFullName"]; | ||
|
||
[o release]; | ||
[e release]; | ||
} | ||
|
||
void mixedLegacy(void) | ||
{ | ||
Observer *o = [Observer new]; | ||
ObserveeMixed *e = [ObserveeMixed new]; | ||
|
||
NSArray *keys = [NSArray arrayWithObjects: @"firstName", @"lastName", nil]; | ||
|
||
[ObserveeMixed setKeys:keys | ||
triggerChangeNotificationsForDependentKey:@"fullName"]; | ||
|
||
NSSet *s = [ObserveeMixed keyPathsForValuesAffectingValueForKey:@"fullName"]; | ||
NSSet *expected = [NSSet setWithObject:@"trigger"]; | ||
PASS_EQUAL(s, expected, "newer API has precedence over deprecated API"); | ||
|
||
|
||
NSKeyValueObservingOptions opts = | ||
NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew; | ||
|
||
[e addObserver:o forKeyPath:@"fullName" options:opts context:NULL]; | ||
|
||
// No trigger | ||
|
||
[e setFirstName:@"Hey"]; | ||
[e setMiddleName:@"Not"]; | ||
[e setLastName:@"You"]; | ||
PASS(o->notificationCount == 0, "no change notification from either firstName, middleName, or lastName"); | ||
|
||
// Trigger | ||
|
||
[e setTrigger:YES]; | ||
PASS(o->notificationCount == 1, "change notification from trigger"); | ||
|
||
[e removeObserver:o forKeyPath:@"fullName"]; | ||
[o release]; | ||
[e release]; | ||
} | ||
|
||
|
||
int | ||
main(int argc, char *argv[]) | ||
{ | ||
START_SET("KVO Legacy Tests") | ||
|
||
simpleDependency(); | ||
registeringMultipleDependencies(); | ||
mixedLegacy(); | ||
|
||
END_SET("KVO Legacy Tests") | ||
|
||
return 0; | ||
} |