|  |  |  | #import <XCTest/XCTest.h> | 
					
						
							|  |  |  | #import "ObservableValue.h" | 
					
						
							|  |  |  | #import "TestUtil.h" | 
					
						
							|  |  |  | #import "CancelTokenSource.h" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface ObservableTest : XCTestCase | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @implementation ObservableTest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | -(void) testObservableAddRemove { | 
					
						
							|  |  |  |     ObservableValueController* s = [ObservableValueController observableValueControllerWithInitialValue:@""]; | 
					
						
							|  |  |  |     ObservableValue* t = s; | 
					
						
							|  |  |  |     NSMutableArray* a = [NSMutableArray array]; | 
					
						
							|  |  |  |     CancelTokenSource* c = [CancelTokenSource cancelTokenSource]; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     [t watchLatestValueOnArbitraryThread:^(id value) {[a addObject:value];} | 
					
						
							|  |  |  |                           untilCancelled:[c getToken]]; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     test([a isEqualToArray:@[@""]]); | 
					
						
							|  |  |  |     [s updateValue:@5]; | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[@"", @5])]); | 
					
						
							|  |  |  |     [s updateValue:@7]; | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[@"", @5, @7])]); | 
					
						
							|  |  |  |     [c cancel]; | 
					
						
							|  |  |  |     [s updateValue:@11]; | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[@"", @5, @7])]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | -(void) testObservableAddAdd { | 
					
						
							|  |  |  |     ObservableValueController* s = [ObservableValueController observableValueControllerWithInitialValue:@""]; | 
					
						
							|  |  |  |     ObservableValue* t = s; | 
					
						
							|  |  |  |     NSMutableArray* a = [NSMutableArray array]; | 
					
						
							|  |  |  |     CancelTokenSource* c = [CancelTokenSource cancelTokenSource]; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     [t watchLatestValueOnArbitraryThread:^(id value) {[a addObject:value];} | 
					
						
							|  |  |  |                           untilCancelled:[c getToken]]; | 
					
						
							|  |  |  |     [t watchLatestValueOnArbitraryThread:^(id value) {[a addObject:value];} | 
					
						
							|  |  |  |                           untilCancelled:[c getToken]]; | 
					
						
							|  |  |  |     [t watchLatestValueOnArbitraryThread:^(id value) {[a addObject:value];} | 
					
						
							|  |  |  |                           untilCancelled:[c getToken]]; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[@"", @"", @""])]); | 
					
						
							|  |  |  |     [s updateValue:@5]; | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[@"", @"", @"", @5, @5, @5])]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | -(void) testObservableRedundantSetIgnored { | 
					
						
							|  |  |  |     id v1 = @""; | 
					
						
							|  |  |  |     id v2 = nil; | 
					
						
							|  |  |  |     id v3 = @1; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     ObservableValueController* s = [ObservableValueController observableValueControllerWithInitialValue:v1]; | 
					
						
							|  |  |  |     ObservableValue* t = s; | 
					
						
							|  |  |  |     __block id latest = nil; | 
					
						
							|  |  |  |     __block int count = 0; | 
					
						
							|  |  |  |     [t watchLatestValueOnArbitraryThread:^(id value) {latest = value;count++;} | 
					
						
							|  |  |  |                           untilCancelled:nil]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test(latest == v1); | 
					
						
							|  |  |  |     test(count == 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [s updateValue:v1]; | 
					
						
							|  |  |  |     test(latest == v1); | 
					
						
							|  |  |  |     test(count == 1); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     [s updateValue:v2]; | 
					
						
							|  |  |  |     test(latest == v2); | 
					
						
							|  |  |  |     test(count == 2); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     [s updateValue:v2]; | 
					
						
							|  |  |  |     test(latest == v2); | 
					
						
							|  |  |  |     test(count == 2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [s updateValue:v1]; | 
					
						
							|  |  |  |     test(latest == v1); | 
					
						
							|  |  |  |     test(count == 3); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     [s updateValue:v3]; | 
					
						
							|  |  |  |     test(latest == v3); | 
					
						
							|  |  |  |     test(count == 4); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | -(void) testObservableReentrantAdd { | 
					
						
							|  |  |  |     ObservableValueController* s = [ObservableValueController observableValueControllerWithInitialValue:@""]; | 
					
						
							|  |  |  |     ObservableValue* t = s; | 
					
						
							|  |  |  |     NSMutableArray* a = [NSMutableArray array]; | 
					
						
							|  |  |  |     CancelTokenSource* c = [CancelTokenSource cancelTokenSource]; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     __block void(^registerSelf)() = nil; | 
					
						
							|  |  |  |     void(^registerSelf_builder)() = ^{ | 
					
						
							|  |  |  |         __block bool first = true; | 
					
						
							|  |  |  |         [t watchLatestValueOnArbitraryThread:^(id value) { | 
					
						
							|  |  |  |             if (!first) registerSelf(); | 
					
						
							|  |  |  |             first = false; | 
					
						
							|  |  |  |             [a addObject:value]; | 
					
						
							|  |  |  |         } untilCancelled:[c getToken]]; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     registerSelf = [registerSelf_builder copy]; | 
					
						
							|  |  |  |     registerSelf(); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // adding during a callback counts as adding after the callback | 
					
						
							|  |  |  |     // so we should see a doubling each time | 
					
						
							|  |  |  |     test([a isEqualToArray:@[@""]]); | 
					
						
							|  |  |  |     [s updateValue:@1]; | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[@"", @1, @1])]); | 
					
						
							|  |  |  |     [s updateValue:@2]; | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[@"", @1, @1, @2, @2, @2, @2])]); | 
					
						
							|  |  |  |     [s updateValue:@3]; | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[@"", @1, @1, @2, @2, @2, @2, @3, @3, @3, @3, @3, @3, @3, @3])]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | -(void) testObservableReentrantRemove { | 
					
						
							|  |  |  |     ObservableValueController* s = [ObservableValueController observableValueControllerWithInitialValue:@""]; | 
					
						
							|  |  |  |     ObservableValue* t = s; | 
					
						
							|  |  |  |     NSMutableArray* a = [NSMutableArray array]; | 
					
						
							|  |  |  |     CancelTokenSource* c = [CancelTokenSource cancelTokenSource]; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     for (int i = 0; i < 3; i++) { | 
					
						
							|  |  |  |         __block bool first = true; | 
					
						
							|  |  |  |         [t watchLatestValueOnArbitraryThread:^(id value) { | 
					
						
							|  |  |  |             if (!first) { | 
					
						
							|  |  |  |                 [c cancel]; | 
					
						
							|  |  |  |                 [a addObject:value]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             first = false; | 
					
						
							|  |  |  |         } untilCancelled:[c getToken]]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // removing during a callback counts as removing after the callback | 
					
						
							|  |  |  |     // so we should see all the callbacks run, then they're all cancelled | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[])]); | 
					
						
							|  |  |  |     [s updateValue:@1]; | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[@1, @1, @1])]); | 
					
						
							|  |  |  |     [s updateValue:@2]; | 
					
						
							|  |  |  |     test([a isEqualToArray:(@[@1, @1, @1])]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end |