Skip to content
Browse files

Now includes unit tests for AQNotifyingBitfield. I'm on a roll, bitch…

…ez *throws up the horns*
  • Loading branch information...
1 parent 7ed9c7c commit 70c15ef41c48b2fb6c6f9dafa0076cb787fa9849 Jim Dovey committed Jun 22, 2011
View
12 AQAppStateMachine.xcodeproj/project.pbxproj
@@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */
3821C49D13AFC43D00175CEE /* AQBitfieldTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3821C49C13AFC43D00175CEE /* AQBitfieldTests.m */; };
3821C4A113B2322400175CEE /* AQBitfieldPredicateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3821C4A013B2322400175CEE /* AQBitfieldPredicateTests.m */; };
+ 3821C4A413B23C8500175CEE /* AQRangeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3821C4A313B23C8500175CEE /* AQRangeTests.m */; };
+ 3821C4A713B240A900175CEE /* AQNotifyingBitfieldTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3821C4A613B240A900175CEE /* AQNotifyingBitfieldTests.m */; };
38431B5813A7C26800178A7E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38431B5713A7C26800178A7E /* Foundation.framework */; };
38431B6313A7C26800178A7E /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38431B6213A7C26800178A7E /* SenTestingKit.framework */; };
38431B6613A7C26900178A7E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38431B5713A7C26800178A7E /* Foundation.framework */; };
@@ -108,6 +110,10 @@
3821C49C13AFC43D00175CEE /* AQBitfieldTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQBitfieldTests.m; sourceTree = "<group>"; };
3821C49F13B2322400175CEE /* AQBitfieldPredicateTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQBitfieldPredicateTests.h; sourceTree = "<group>"; };
3821C4A013B2322400175CEE /* AQBitfieldPredicateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQBitfieldPredicateTests.m; sourceTree = "<group>"; };
+ 3821C4A213B23C8500175CEE /* AQRangeTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQRangeTests.h; sourceTree = "<group>"; };
+ 3821C4A313B23C8500175CEE /* AQRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQRangeTests.m; sourceTree = "<group>"; };
+ 3821C4A513B240A900175CEE /* AQNotifyingBitfieldTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQNotifyingBitfieldTests.h; sourceTree = "<group>"; };
+ 3821C4A613B240A900175CEE /* AQNotifyingBitfieldTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQNotifyingBitfieldTests.m; sourceTree = "<group>"; };
38431B5413A7C26800178A7E /* libAQAppStateMachine.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libAQAppStateMachine.a; sourceTree = BUILT_PRODUCTS_DIR; };
38431B5713A7C26800178A7E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
38431B5B13A7C26800178A7E /* AQAppStateMachine-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AQAppStateMachine-Prefix.pch"; sourceTree = "<group>"; };
@@ -272,6 +278,10 @@
3821C49C13AFC43D00175CEE /* AQBitfieldTests.m */,
3821C49F13B2322400175CEE /* AQBitfieldPredicateTests.h */,
3821C4A013B2322400175CEE /* AQBitfieldPredicateTests.m */,
+ 3821C4A213B23C8500175CEE /* AQRangeTests.h */,
+ 3821C4A313B23C8500175CEE /* AQRangeTests.m */,
+ 3821C4A513B240A900175CEE /* AQNotifyingBitfieldTests.h */,
+ 3821C4A613B240A900175CEE /* AQNotifyingBitfieldTests.m */,
386693EF13AF8A1500268560 /* SortedDictionary */,
38431B6D13A7C26900178A7E /* Supporting Files */,
);
@@ -573,6 +583,8 @@
3866946013AFB77500268560 /* AQAdditions.m in Sources */,
3821C49D13AFC43D00175CEE /* AQBitfieldTests.m in Sources */,
3821C4A113B2322400175CEE /* AQBitfieldPredicateTests.m in Sources */,
+ 3821C4A413B23C8500175CEE /* AQRangeTests.m in Sources */,
+ 3821C4A713B240A900175CEE /* AQNotifyingBitfieldTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
91 AQAppStateMachine/AQNotifyingBitfield.m
@@ -54,99 +54,89 @@ - (void) removeNotifierForBitsInRange: (NSRange) range
- (void) removeAllNotifiersWithinRange: (NSRange) range
{
dispatch_async(_syncQ, ^{
+ NSMutableSet * keys = [NSMutableSet new];
+
[_lookup enumerateKeysAndObjectsUsingBlock: ^(__strong id key, __strong id obj, BOOL *stop) {
- NSRange testRange = [obj range];
+ NSRange testRange = [key range];
if ( NSEqualRanges(testRange, NSIntersectionRange(range, testRange)) == NO )
{
if ( testRange.location > NSMaxRange(range) )
*stop = YES;
return; // not wholly contained in the input range
}
- [_lookup removeObjectForKey: obj];
+ [keys addObject: key];
}];
+
+ for ( id key in keys )
+ {
+ [_lookup removeObjectForKey: key];
+ }
});
}
-- (void) _scheduleNotificationForRange: (AQRange *) range
+- (void) _scheduleNotificationsForIndex: (NSUInteger) index
{
- dispatch_async(dispatch_get_global_queue(0, 0), ^{
- AQRangeNotification block = (AQRangeNotification)[_lookup objectForKey: range];
- if ( block != nil )
- block(range.range);
- });
-}
-
-- (void) flipBitAtIndex: (NSUInteger) index
-{
- [super flipBitAtIndex: index];
-
dispatch_async(_syncQ, ^{
[_lookup enumerateKeysAndObjectsUsingBlock: ^(__strong id key, __strong id obj, BOOL *stop) {
- if ( NSLocationInRange(index, [obj range]) )
+ if ( NSLocationInRange(index, [key range]) )
{
- [self _scheduleNotificationForRange: obj];
+ AQRangeNotification block = (AQRangeNotification)obj;
+ if ( block != nil )
+ {
+ dispatch_async(dispatch_get_global_queue(0, 0), ^{ block([key range]); });
+ }
}
- else if ( index < [obj range].location )
+ else if ( index < [key range].location )
{
*stop = YES;
}
}];
});
}
-- (void) flipBitsInRange: (NSRange) range
+- (void) _scheduleNotificationsForRange: (NSRange) range
{
- [super flipBitsInRange: range];
-
dispatch_async(_syncQ, ^{
[_lookup enumerateKeysAndObjectsUsingBlock: ^(__strong id key, __strong id obj, BOOL *stop) {
- if ( NSIntersectionRange(range, [obj range]).location != NSNotFound )
+ if ( NSIntersectionRange(range, [key range]).length != 0 )
{
- [self _scheduleNotificationForRange: obj];
+ AQRangeNotification block = (AQRangeNotification)obj;
+ if ( block != nil )
+ {
+ dispatch_async(dispatch_get_global_queue(0, 0), ^{ block([key range]); });
+ }
}
- else if ( NSMaxRange(range) < [obj range].location )
+ else if ( NSMaxRange(range) < [key range].location )
{
*stop = YES;
}
}];
});
}
+- (void) flipBitAtIndex: (NSUInteger) index
+{
+ [super flipBitAtIndex: index];
+ [self _scheduleNotificationsForIndex: index];
+}
+
+- (void) flipBitsInRange: (NSRange) range
+{
+ [super flipBitsInRange: range];
+ [self _scheduleNotificationsForRange: range];
+}
+
- (void) setBit: (AQBit) bit atIndex: (NSUInteger) index
{
[super setBit: bit atIndex: index];
-
- dispatch_async(_syncQ, ^{
- [_lookup enumerateKeysAndObjectsUsingBlock: ^(__strong id key, __strong id obj, BOOL *stop) {
- if ( NSLocationInRange(index, [obj range]) )
- {
- [self _scheduleNotificationForRange: obj];
- }
- else if ( index < [obj range].location )
- {
- *stop = YES;
- }
- }];
- });
+ [self _scheduleNotificationsForIndex: index];
}
- (void) setBitsInRange: (NSRange) range usingBit: (AQBit) bit
{
[super setBitsInRange: range usingBit: bit];
-
- dispatch_async(_syncQ, ^{
- [_lookup enumerateKeysAndObjectsUsingBlock: ^(__strong id key, __strong id obj, BOOL *stop) {
- if ( NSIntersectionRange(range, [obj range]).location != NSNotFound )
- {
- [self _scheduleNotificationForRange: obj];
- }
- else if ( NSMaxRange(range) < [obj range].location )
- {
- *stop = YES;
- }
- }];
- });
+ [self _scheduleNotificationsForRange: range];
}
- (void) setAllBits: (AQBit) bit
@@ -156,7 +146,8 @@ - (void) setAllBits: (AQBit) bit
// always scheduling for all bits
dispatch_async(_syncQ, ^{
[_lookup enumerateKeysAndObjectsUsingBlock: ^(__strong id key, __strong id obj, BOOL *stop) {
- [self _scheduleNotificationForRange: obj];
+ AQRangeNotification block = (AQRangeNotification)obj;
+ dispatch_async(dispatch_get_global_queue(0, 0), ^{ block([key range]); });
}];
});
}
View
2 AQAppStateMachine/AQRange.h
@@ -8,7 +8,7 @@
#import <Foundation/Foundation.h>
-@interface AQRange : NSObject
+@interface AQRange : NSObject <NSCopying>
{
NSRange _range;
}
View
12 AQAppStateMachine/AQRange.m
@@ -28,6 +28,16 @@ - (id) initWithRange: (NSRange) range
return ( self );
}
+- (NSString *) description
+{
+ return ( NSStringFromRange(_range) );
+}
+
+- (id) copyWithZone: (NSZone *) zone
+{
+ return ( [[AQRange alloc] initWithRange: _range] );
+}
+
- (NSUInteger) hash
{
return ( _range.location << 16 | _range.length );
@@ -64,7 +74,7 @@ - (NSComparisonResult) compareToNSRange: (NSRange) nsRange
// locations are identical, but lengths are not
// in this case, we order by which is *finished* first, i.e. one with the lowest length
- if ( _range.length > nsRange.length )
+ if ( _range.length < nsRange.length )
return ( NSOrderedAscending );
return ( NSOrderedDescending );
View
20 AQAppStateMachineTests/AQNotifyingBitfieldTests.h
@@ -0,0 +1,20 @@
+//
+// AQNotifyingBitfieldTests.h
+// AQAppStateMachine
+//
+// Created by Jim Dovey on 11-06-22.
+// Copyright 2011 Jim Dovey. All rights reserved.
+//
+// See Also: http://developer.apple.com/iphone/library/documentation/Xcode/Conceptual/iphone_development/135-Unit_Testing_Applications/unit_testing_applications.html
+
+// Application unit tests contain unit test code that must be injected into an application to run correctly.
+// Define USE_APPLICATION_UNIT_TEST to 0 if the unit test code is designed to be linked into an independent test executable.
+
+#import <SenTestingKit/SenTestingKit.h>
+#import <UIKit/UIKit.h>
+//#import "application_headers" as required
+@class AQNotifyingBitfield;
+
+@interface AQNotifyingBitfieldTests : SenTestCase
+@property (nonatomic, retain) AQNotifyingBitfield * bitfield;
+@end
View
123 AQAppStateMachineTests/AQNotifyingBitfieldTests.m
@@ -0,0 +1,123 @@
+//
+// AQNotifyingBitfieldTests.m
+// AQAppStateMachine
+//
+// Created by Jim Dovey on 11-06-22.
+// Copyright 2011 Jim Dovey. All rights reserved.
+//
+
+#import "AQNotifyingBitfieldTests.h"
+#import "AQNotifyingBitfield.h"
+
+@implementation AQNotifyingBitfieldTests
+
+@synthesize bitfield;
+
+- (void) setUp
+{
+ AQNotifyingBitfield * tmp = [AQNotifyingBitfield new];
+ [tmp setBitsInRange: NSMakeRange(0, 10) usingBit: 1];
+ self.bitfield = tmp;
+}
+
+- (void) testSingleNotificationForRange
+{
+ __block BOOL notified = NO;
+ [self.bitfield notifyModificationOfBitsInRange: NSMakeRange(0, 5) usingBlock: ^(NSRange range) {
+ notified = YES;
+ }];
+
+ [self.bitfield flipBitAtIndex: 8];
+ STAssertFalse(notified, @"Should not have been notified for change outside of notification range");
+
+ [self.bitfield flipBitAtIndex: 4];
+ [NSThread sleepForTimeInterval: 0.1];
+ STAssertTrue(notified, @"Should have been notified for change within notification range");
+}
+
+- (void) testMultipleNotificationsForRange
+{
+ __block NSUInteger notifications1 = 0;
+ __block NSUInteger notifications2 = 0;
+ [self.bitfield notifyModificationOfBitsInRange: NSMakeRange(0, 5) usingBlock: ^(NSRange range) {
+ notifications1 += 1;
+ }];
+ [self.bitfield notifyModificationOfBitsInRange: NSMakeRange(3, 5) usingBlock: ^(NSRange range) {
+ notifications2 += 1;
+ }];
+
+ [self.bitfield flipBitAtIndex: 8];
+ STAssertTrue(notifications1 == 0, @"Notification for 0..4 should not fire for modification at 8");
+ STAssertTrue(notifications2 == 0, @"Notification for 3..7 should not fire for modification at 8");
+
+ [self.bitfield flipBitAtIndex: 0];
+ [NSThread sleepForTimeInterval: 0.1];
+ STAssertTrue(notifications1 == 1, @"Flip at bit 0 should trigger notification for bits 0..4");
+ STAssertTrue(notifications2 == 0, @"Flip at bit 0 should NOT trigger notification for bits 3..7");
+
+ [self.bitfield flipBitAtIndex: 6];
+ [NSThread sleepForTimeInterval: 0.1];
+ STAssertTrue(notifications1 == 1, @"Flip at bit 6 should NOT trigger notification for bits 0..4");
+ STAssertTrue(notifications2 == 1, @"Flip at bit 6 should trigger notification for bits 3..7");
+
+ [self.bitfield flipBitAtIndex: 4];
+ [NSThread sleepForTimeInterval: 0.1];
+ STAssertTrue(notifications1 == 2, @"Flip at bit 4 should trigger notification for bits 0..4");
+ STAssertTrue(notifications2 == 2, @"Flip at bit 4 should trigger notification for bits 3..7");
+}
+
+- (void) testRangeModifiedNotifications
+{
+ __block BOOL notified = NO;
+ [self.bitfield notifyModificationOfBitsInRange: NSMakeRange(3, 10) usingBlock: ^(NSRange range) {
+ notified = YES;
+ }];
+
+ [self.bitfield setBitsInRange: NSMakeRange(20, 5) usingBit: 1];
+ [NSThread sleepForTimeInterval: 0.1];
+ STAssertFalse(notified, @"Modifying bits 20..24 should NOT trigger notification for bits 3..12");
+
+ [self.bitfield setBitsInRange: NSMakeRange(8, 10) usingBit: 0];
+ [NSThread sleepForTimeInterval: 0.1];
+ STAssertTrue(notified, @"Modifying bits 8..17 should trigger notification for bits 3..12");
+}
+
+- (void) testNotificationRemoval
+{
+ __block BOOL notified = NO;
+ [self.bitfield notifyModificationOfBitsInRange: NSMakeRange(0, 5) usingBlock: ^(NSRange range) {
+ notified = YES;
+ }];
+
+ [self.bitfield flipBitAtIndex: 4];
+ [NSThread sleepForTimeInterval: 0.1];
+ STAssertTrue(notified, @"Should have been notified for change within notification range");
+
+ notified = NO;
+ [self.bitfield removeNotifierForBitsInRange: NSMakeRange(0, 5)];
+
+ [self.bitfield flipBitAtIndex: 4];
+ [NSThread sleepForTimeInterval: 0.1];
+ STAssertFalse(notified, @"Should NOT have been notified for change within a removed notification range");
+}
+
+- (void) testNotificationBulkRemoval
+{
+ __block BOOL notified = NO;
+ [self.bitfield notifyModificationOfBitsInRange: NSMakeRange(5, 5) usingBlock: ^(NSRange range) {
+ notified = YES;
+ }];
+
+ [self.bitfield flipBitAtIndex: 8];
+ [NSThread sleepForTimeInterval: 0.1];
+ STAssertTrue(notified, @"Should have been notified for change within notification range");
+
+ notified = NO;
+ [self.bitfield removeAllNotifiersWithinRange: NSMakeRange(0, 20)];
+
+ [self.bitfield flipBitAtIndex: 8];
+ [NSThread sleepForTimeInterval: 0.1];
+ STAssertFalse(notified, @"Should NOT have been notified for change within a removed notification range");
+}
+
+@end
View
20 AQAppStateMachineTests/AQRangeTests.h
@@ -0,0 +1,20 @@
+//
+// AQRangeTests.h
+// AQAppStateMachine
+//
+// Created by Jim Dovey on 11-06-22.
+// Copyright 2011 Jim Dovey. All rights reserved.
+//
+// See Also: http://developer.apple.com/iphone/library/documentation/Xcode/Conceptual/iphone_development/135-Unit_Testing_Applications/unit_testing_applications.html
+
+// Application unit tests contain unit test code that must be injected into an application to run correctly.
+// Define USE_APPLICATION_UNIT_TEST to 0 if the unit test code is designed to be linked into an independent test executable.
+
+#define USE_APPLICATION_UNIT_TEST 1
+
+#import <SenTestingKit/SenTestingKit.h>
+#import <UIKit/UIKit.h>
+
+@interface AQRangeTests : SenTestCase
+
+@end
View
42 AQAppStateMachineTests/AQRangeTests.m
@@ -0,0 +1,42 @@
+//
+// AQRangeTests.m
+// AQAppStateMachine
+//
+// Created by Jim Dovey on 11-06-22.
+// Copyright 2011 Jim Dovey. All rights reserved.
+//
+
+#import "AQRangeTests.h"
+#import "AQRange.h"
+
+@implementation AQRangeTests
+
+- (void) testEquality
+{
+ AQRange * range = [[AQRange alloc] initWithRange: NSMakeRange(0, 10)];
+ AQRange * equal = [[AQRange alloc] initWithRange: NSMakeRange(0, 10)];
+ AQRange * fail = [[AQRange alloc] initWithRange: NSMakeRange(0, 20)];
+
+ STAssertEqualObjects(range, equal, @"Expected %@ to equal %@", range, equal);
+ STAssertFalse([range isEqual: fail], @"Expected %@ to NOT equal %@", range, fail);
+ STAssertFalse([range isEqual: @"Hello"], @"Expected %@ to NOT equal the string 'Hello'", range);
+ STAssertTrue([range isEqualToNSRange: equal.range], @"Expected %@ to equal %@", range, NSStringFromRange(equal.range));
+ STAssertFalse([range isEqualToNSRange: fail.range], @"Expected %@ to NOT equal %@", range, fail.range);
+}
+
+- (void) testComparisons
+{
+ AQRange * range = [[AQRange alloc] initWithRange: NSMakeRange(0, 10)];
+ AQRange * equal = [[AQRange alloc] initWithRange: NSMakeRange(0, 10)];
+ AQRange * higherForLength = [[AQRange alloc] initWithRange: NSMakeRange(0, 20)];
+ AQRange * higherForLocation = [[AQRange alloc] initWithRange: NSMakeRange(10, 10)];
+
+ STAssertTrue([range compare: equal] == NSOrderedSame, @"Expected comparison of %@ to %@ to return 'same', got %lu", range, equal, (unsigned long)[range compare: equal]);
+ STAssertTrue([range compare: higherForLength] == NSOrderedAscending, @"Expected comparison of %@ to %@ to return 'ascending', got %lu", range, higherForLength, (unsigned long)[range compare: higherForLength]);
+ STAssertTrue([higherForLength compare: range] == NSOrderedDescending, @"Expected comparison of %@ to %@ to return 'ascending', got %lu", higherForLength, range, (unsigned long)[higherForLength compare: range]);
+ STAssertTrue([range compare: higherForLocation] == NSOrderedAscending, @"Expected comparison of %@ to %@ to return 'ascending', got %lu", range, higherForLocation, (unsigned long)[range compare: higherForLocation]);
+ STAssertTrue([higherForLocation compare: range] == NSOrderedDescending, @"Expected comparison of %@ to %@ to return 'ascending', got %lu", higherForLocation, range, (unsigned long)[higherForLocation compare: range]);
+ STAssertTrue([range compareToNSRange: higherForLength.range] == NSOrderedAscending, @"Expected comparison of %@ to %@ to return 'ascending', got %lu", range, NSStringFromRange(higherForLength.range), (unsigned long)[range compareToNSRange: higherForLength.range]);
+}
+
+@end

0 comments on commit 70c15ef

Please sign in to comment.
Something went wrong with that request. Please try again.