From 758ad3eb06adc9cf5fe356bbcd0240610539eae2 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Fri, 20 Nov 2015 12:43:53 +0100 Subject: [PATCH] Fixed: Method types was not copied into new KVO implementation of a class that is observed with KVO. Some test cases are also added for this. --- Foundation/CPKeyValueObserving.j | 26 ++++----- Tests/Foundation/CPKeyValueObservingTest.j | 63 +++++++++++++++++++++- 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/Foundation/CPKeyValueObserving.j b/Foundation/CPKeyValueObserving.j index b3f01f05a..95c00d36a 100644 --- a/Foundation/CPKeyValueObserving.j +++ b/Foundation/CPKeyValueObserving.j @@ -423,7 +423,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti setKey_method_imp(self, _cmd, anObject); [self didChangeValueForKey:aKey]; - }, ""); + }, setKey_method.method_types); } // FIXME: Deprecated. @@ -441,7 +441,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti _setKey_method_imp(self, _cmd, anObject); [self didChangeValueForKey:aKey]; - }, ""); + }, _setKey_method.method_types); } // Ordered To-Many Relationships @@ -478,7 +478,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] forKey:aKey]; - }, ""); + }, insertObject_inKeyAtIndex_method.method_types); } if (insertKey_atIndexes_method) @@ -496,7 +496,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChange:CPKeyValueChangeInsertion valuesAtIndexes:[indexes copy] forKey:aKey]; - }, ""); + }, insertKey_atIndexes_method.method_types); } if (removeObjectFromKeyAtIndex_method) @@ -514,7 +514,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] forKey:aKey]; - }, ""); + }, removeObjectFromKeyAtIndex_method.method_types); } if (removeKeyAtIndexes_method) @@ -532,7 +532,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChange:CPKeyValueChangeRemoval valuesAtIndexes:[indexes copy] forKey:aKey]; - }, ""); + }, removeKeyAtIndexes_method.method_types); } // These are optional. @@ -558,7 +558,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] forKey:aKey]; - }, ""); + }, replaceObjectInKeyAtIndex_withObject_method.method_types); } var replaceKeyAtIndexes_withKey_selector = @@ -581,7 +581,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChange:CPKeyValueChangeReplacement valuesAtIndexes:[indexes copy] forKey:aKey]; - }, ""); + }, replaceKeyAtIndexes_withKey_method.method_types); } } @@ -615,7 +615,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChangeValueForKey:aKey withSetMutation:CPKeyValueUnionSetMutation usingObjects:[CPSet setWithObject:anObject]]; - }, ""); + }, addKeyObject_method.method_types); } if (addKey_method) @@ -633,7 +633,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChangeValueForKey:aKey withSetMutation:CPKeyValueUnionSetMutation usingObjects:[objects copy]]; - }, ""); + }, addKey_method.method_types); } if (removeKeyObject_method) @@ -651,7 +651,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChangeValueForKey:aKey withSetMutation:CPKeyValueMinusSetMutation usingObjects:[CPSet setWithObject:anObject]]; - }, ""); + }, removeKeyObject_method.method_types); } if (removeKey_method) @@ -669,7 +669,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChangeValueForKey:aKey withSetMutation:CPKeyValueMinusSetMutation usingObjects:[objects copy]]; - }, ""); + }, removeKey_method.method_types); } // intersect: is optional. @@ -691,7 +691,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti [self didChangeValueForKey:aKey withSetMutation:CPKeyValueIntersectSetMutation usingObjects:[aSet copy]]; - }, ""); + }, intersectKey_method.method_types); } } diff --git a/Tests/Foundation/CPKeyValueObservingTest.j b/Tests/Foundation/CPKeyValueObservingTest.j index 9726a7f05..59b501b1c 100644 --- a/Tests/Foundation/CPKeyValueObservingTest.j +++ b/Tests/Foundation/CPKeyValueObservingTest.j @@ -28,7 +28,7 @@ var _getCheeseCounter; count = arguments.length; for (; index < count; ++index) - class_addMethod(theClass, arguments[index], function() { }); + class_addMethod(theClass, arguments[index], function() { }, ["void"]); return [theClass new]; } @@ -268,7 +268,8 @@ var _getCheeseCounter; - (void)testOnlyInsertObject_AtKeyIndex_Implemented { var insertSelector = @selector(insertObject:inObjectsAtIndex:), - object = [self objectWithMethods:insertSelector]; + object = [self objectWithMethods:insertSelector], + methodTypes = class_getInstanceMethod(object.isa, insertSelector).method_types; // Sanity check [self assert:class_getInstanceMethod(object.isa, insertSelector) @@ -417,6 +418,64 @@ var _getCheeseCounter; [self assert:test equals:_lastObject]; } +- (void)testMethodTypesOnKVOForSet_Key_Implemented +{ + var setSelector = @selector(setObjects:), + object = [self objectWithMethods:setSelector], + methodTypes = class_getInstanceMethod(object.isa, setSelector).method_types; + + // Sanity check + [self assert:class_getInstanceMethod(object.isa, setSelector) + same:class_getInstanceMethod([object class], setSelector)]; + + [object + addObserver:self + forKeyPath:@"objects" + options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew + context:NULL]; + + // Sanity check + [self assert:class_getInstanceMethod(object.isa, setSelector) + notSame:class_getInstanceMethod([object class], setSelector)]; + + // Check that the return and parameter types are the same on the new method as the old + [self assertTrue:methodTypes != nil message:@"methodTypes can not be nil or undefined"]; + [self assert:methodTypes equals:class_getInstanceMethod(object.isa, setSelector).method_types]; +} + +- (void)testMethodTypesOnKVOForInsertObject_AtKeyIndex_Implemented_and_removeFromKeyAtIndex +{ + var insertSelector = @selector(insertObject:inObjectsAtIndex:), + removeSelector = @selector(removeObjectFromObjectsAtIndex:), + object = [self objectWithMethods:insertSelector, removeSelector], + methodTypesInsert = class_getInstanceMethod(object.isa, insertSelector).method_types, + methodTypesRemove = class_getInstanceMethod(object.isa, removeSelector).method_types; + + // Sanity check + [self assert:class_getInstanceMethod(object.isa, insertSelector) + same:class_getInstanceMethod([object class], insertSelector)]; + [self assert:class_getInstanceMethod(object.isa, removeSelector) + same:class_getInstanceMethod([object class], removeSelector)]; + + [object + addObserver:self + forKeyPath:@"objects" + options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew + context:NULL]; + + // Sanity check + [self assert:class_getInstanceMethod(object.isa, insertSelector) + notSame:class_getInstanceMethod([object class], insertSelector)]; + [self assert:class_getInstanceMethod(object.isa, removeSelector) + notSame:class_getInstanceMethod([object class], removeSelector)]; + + // Check that the return and parameter types are the same on the new method as the old + [self assertTrue:methodTypesInsert != nil message:@"methodTypes can not be nil or undefined on insert selector"]; + [self assertTrue:methodTypesRemove != nil message:@"methodTypes can not be nil or undefined in remove selector"]; + [self assert:methodTypesInsert equals:class_getInstanceMethod(object.isa, insertSelector).method_types]; + [self assert:methodTypesRemove equals:class_getInstanceMethod(object.isa, removeSelector).method_types]; +} + @end @implementation ObservingTester : CPObject