ComponentKitTests/Scope/CKComponentScopeTests.mm (715 lines of code) (raw):
/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <XCTest/XCTest.h>
#import <ComponentKit/CKCompositeComponent.h>
#import <ComponentKit/CKCollection.h>
#import <ComponentKit/CKComponentScope.h>
#import <ComponentKit/CKComponentScopeRoot.h>
#import <ComponentKit/CKComponentScopeRootFactory.h>
#import <ComponentKit/CKComponentProtocol.h>
#import <ComponentKit/CKComponentControllerProtocol.h>
#import <ComponentKit/CKComponentInternal.h>
#import <ComponentKit/CKRenderComponent.h>
#import <ComponentKit/CKRootTreeNode.h>
#import <ComponentKit/CKThreadLocalComponentScope.h>
#import <ComponentKit/CKTreeNode.h>
@protocol TestScopedProtocol <NSObject>
@end
@interface TestComponentWithScopedProtocol : NSObject <CKComponentProtocol, TestScopedProtocol>
@end
@implementation TestComponentWithScopedProtocol
+ (id)initialState { return nil; }
+ (Class<CKComponentControllerProtocol>)controllerClass
{ return nil; };
- (id<CKComponentControllerProtocol>)buildController
{ return nil; }
- (NSString *)className
{ return @""; }
- (const char *)typeName
{ return ""; }
+ (RCComponentCoalescingMode)coalescingMode
{ return RCComponentCoalescingModeNone; }
- (void)buildComponentTree:(CKTreeNode *)parent previousParent:(CKTreeNode *)previousParent params:(const CKBuildComponentTreeParams &)params parentHasStateUpdate:(BOOL)parentHasStateUpdate { }
- (unsigned int)numberOfChildren { return 0; }
- (id<CKComponentProtocol>)childAtIndex:(unsigned int)index { return nil; }
- (CKTreeNode *)treeNode { return nil; }
- (void)acquireTreeNode:(CKTreeNode *)treeNode { }
@end
@interface TestComponentWithoutScopedProtocol : NSObject <CKComponentProtocol>
@end
@implementation TestComponentWithoutScopedProtocol
+ (id)initialState { return nil; }
+ (Class<CKComponentControllerProtocol>)controllerClass
{ return nil; };
- (id<CKComponentControllerProtocol>)buildController
{ return nil; }
- (NSString *)className
{ return @""; }
- (const char *)typeName
{ return ""; }
+ (RCComponentCoalescingMode)coalescingMode
{ return RCComponentCoalescingModeNone; }
- (void)buildComponentTree:(CKTreeNode *)parent previousParent:(CKTreeNode *)previousParent params:(const CKBuildComponentTreeParams &)params parentHasStateUpdate:(BOOL)parentHasStateUpdate { }
- (id<CKComponentProtocol>)childAtIndex:(unsigned int)index { return nil; }
- (CKTreeNode *)treeNode { return nil; }
- (void)acquireTreeNode:(CKTreeNode *)treeNode { }
- (unsigned int)numberOfChildren { return 0; }
@end
@interface TestComponentControllerWithScopedProtocol : NSObject <CKComponentControllerProtocol, TestScopedProtocol>
@end
@implementation TestComponentControllerWithScopedProtocol
- (instancetype)initWithComponent:(id<CKComponentProtocol>)component
{
return [super init];
}
- (id<CKComponentProtocol>)component { return [CKComponent new]; }
@end
@interface CKComponentScopeTests : XCTestCase
@end
@implementation CKComponentScopeTests
#pragma mark - Thread Local Component Scope
- (void)testThreadLocalComponentScopeIsEmptyWhenNoScopeExists
{
XCTAssertTrue(CKThreadLocalComponentScope::currentScope() == nullptr);
}
- (void)testThreadLocalComponentScopeIsNotEmptyWhenTheScopeExists
{
CKThreadLocalComponentScope threadScope(CKComponentScopeRootWithDefaultPredicates(nil, nil), {});
XCTAssertTrue(CKThreadLocalComponentScope::currentScope() != nullptr);
}
- (void)testThreadLocalComponentScopeStoresTheProvidedFrameAsTheEquivalentPreviousFrame
{
CKComponentScopeRoot *root = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKThreadLocalComponentScope threadScope(root, {});
XCTAssertEqualObjects(CKThreadLocalComponentScope::currentScope()->stack.top().previousNode, root.rootNode.node());
}
- (void)testThreadLocalComponentScopePushesChildComponentScope
{
CKComponentScopeRoot *root = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKThreadLocalComponentScope threadScope(root, {});
CKTreeNode *rootFrame = CKThreadLocalComponentScope::currentScope()->stack.top().node;
CKComponentScope scope([CKCompositeComponent class]);
CKTreeNode *currentFrame = CKThreadLocalComponentScope::currentScope()->stack.top().node;
XCTAssertTrue(currentFrame != rootFrame);
}
- (void)testThreadLocalComponentScopeCanBeNested
{
{
CKComponentScopeRoot *root = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKThreadLocalComponentScope threadScope(root, {});
{
CKComponentScopeRoot *root2 = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKThreadLocalComponentScope threadScope2(root2, {});
XCTAssertEqual(CKThreadLocalComponentScope::currentScope(), &threadScope2);
}
XCTAssertEqual(CKThreadLocalComponentScope::currentScope(), &threadScope);
}
XCTAssertEqual(CKThreadLocalComponentScope::currentScope(), nullptr);
}
#pragma mark - Component Scope Frame
- (void)testComponentScopeFrameIsPoppedWhenComponentScopeCloses
{
CKComponentScopeRoot *root = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKThreadLocalComponentScope threadScope(root, {});
CKTreeNode *rootFrame = CKThreadLocalComponentScope::currentScope()->stack.top().node;
{
CKComponentScope scope([CKCompositeComponent class], @"moose");
XCTAssertTrue(CKThreadLocalComponentScope::currentScope()->stack.top().node != rootFrame);
}
XCTAssertEqual(CKThreadLocalComponentScope::currentScope()->stack.top().node, rootFrame);
}
#pragma mark - Component Scope State
- (void)testComponentScopeStateIsAcquiredFromPreviousComponentScopeStateOneLevelDown
{
CKComponentScopeRoot *root1 = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKComponentScopeRoot *root2;
{
CKThreadLocalComponentScope threadScope(root1, {});
{
CKComponentScope scope([CKCompositeComponent class], @"macaque", ^{ return @42; });
}
root2 = CKThreadLocalComponentScope::currentScope()->newScopeRoot;
}
{
CKThreadLocalComponentScope threadScope(root2, {});
{
// This block should never be called. We should inherit the previous scope.
BOOL __block blockCalled = NO;
CKComponentScope scope([CKCompositeComponent class], @"macaque", ^{
blockCalled = YES;
return @365;
});
id state = scope.state();
XCTAssertFalse(blockCalled);
XCTAssertEqualObjects(state, @42);
}
}
}
- (void)testComponentScopeReplaceStatePropagatesStateToNextComponentScopeState
{
CKComponentScopeRoot *root1 = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKComponentScopeRoot *root2;
{
CKThreadLocalComponentScope threadScope(root1, {});
{
CKComponentScope scope([CKCompositeComponent class], @"macaque", ^{ return @42; });
CKComponentScope::replaceState(scope, @100);
XCTAssertEqualObjects(scope.state(), @100);
}
root2 = CKThreadLocalComponentScope::currentScope()->newScopeRoot;
}
{
CKThreadLocalComponentScope threadScope(root2, {});
{
CKComponentScope scope([CKCompositeComponent class], @"macaque", ^{ return @365; });
XCTAssertEqualObjects(scope.state(), @100);
}
}
}
- (void)testComponentScopeStateIsAcquiredFromPreviousComponentScopeStateOneLevelDownWithSibling
{
CKComponentScopeRoot *root1 = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKComponentScopeRoot *root2;
{
CKThreadLocalComponentScope threadScope(root1, {});
{
CKComponentScope scope([CKCompositeComponent class], @"spongebob", ^{ return @"FUN"; });
id __unused state = scope.state();
}
{
CKComponentScope scope([CKCompositeComponent class], @"patrick", ^{ return @"HAHA"; });
id __unused state = scope.state();
}
root2 = CKThreadLocalComponentScope::currentScope()->newScopeRoot;
}
{
CKThreadLocalComponentScope threadScope(root2, {});
{
// This block should never be called. We should inherit the previous scope.
CKComponentScope scope([CKCompositeComponent class], @"spongebob", ^{ return @"rotlf-nope!"; });
id state = scope.state();
XCTAssertEqualObjects(state, @"FUN");
}
{
// This block should never be called. We should inherit the previous scope.
CKComponentScope scope([CKCompositeComponent class], @"patrick", ^{ return @"rotlf-nope!"; });
id state = scope.state();
XCTAssertEqualObjects(state, @"HAHA");
}
}
}
- (void)testComponentScopeStateIsAcquiredFromPreviousComponentScopeStateOneLevelDownWithSiblingThatDoesNotAcquire
{
CKComponentScopeRoot *root1 = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKComponentScopeRoot *root2;
{
CKThreadLocalComponentScope threadScope(root1, {});
{
CKComponentScope scope([CKCompositeComponent class], @"Quoth", ^{ return @"nevermore"; });
id __unused state = scope.state();
}
{
CKComponentScope scope([CKCompositeComponent class], @"perched", ^{ return @"raven"; });
id __unused state = scope.state();
}
root2 = CKThreadLocalComponentScope::currentScope()->newScopeRoot;
}
{
CKThreadLocalComponentScope threadScope(root2, {});
{
// This block should never be called. We should inherit the previous scope.
CKComponentScope scope([CKCompositeComponent class], @"Quoth", ^{ return @"Lenore"; });
id state = scope.state();
XCTAssertEqualObjects(state, @"nevermore");
}
{
CKComponentScope scope([CKCompositeComponent class], @"chamber", ^{ return @"door"; });
id state = scope.state();
XCTAssertEqualObjects(state, @"door");
}
}
}
#pragma mark - Test Component Scope Identifier
- (void)testComponentScopeIdentifierIsSameAsNodeIdentifier
{
CKComponentScopeRoot *root1 = CKComponentScopeRootWithDefaultPredicates(nil, nil);
{
CKThreadLocalComponentScope threadScope(root1, {});
{
CKComponentScope scope([CKCompositeComponent class], @"macaque");
const auto identifier = CKThreadLocalComponentScope::currentScope()->stack.top().node.nodeIdentifier;
XCTAssertEqual(identifier, scope.identifier());
}
}
}
#pragma mark - Component Scope Handle Global Identifier
- (void)testComponentScopeHandleGlobalIdentifierIsAcquiredFromPreviousComponentScopeOneLevelDown
{
CKComponentScopeRoot *root1 = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKComponentScopeRoot *root2;
int32_t globalIdentifier;
{
CKThreadLocalComponentScope threadScope(root1, {});
{
CKComponentScope scope([CKCompositeComponent class], @"macaque");
globalIdentifier = CKThreadLocalComponentScope::currentScope()->stack.top().node.scopeHandle.globalIdentifier;
}
root2 = CKThreadLocalComponentScope::currentScope()->newScopeRoot;
}
{
CKThreadLocalComponentScope threadScope(root2, {});
{
CKComponentScope scope([CKCompositeComponent class], @"macaque");
XCTAssertEqual(globalIdentifier, CKThreadLocalComponentScope::currentScope()->stack.top().node.scopeHandle.globalIdentifier);
}
}
}
- (void)testComponentScopeHandleGlobalIdentifierIsNotTheSameBetweenSiblings
{
CKComponentScopeRoot *root = CKComponentScopeRootWithDefaultPredicates(nil, nil);
{
CKThreadLocalComponentScope threadScope(root, {});
int32_t globalIdentifier;
{
CKComponentScope scope([CKCompositeComponent class], @"moose");
globalIdentifier = CKThreadLocalComponentScope::currentScope()->stack.top().node.scopeHandle.globalIdentifier;
}
{
CKComponentScope scope([CKCompositeComponent class], @"macaque");
XCTAssertNotEqual(globalIdentifier, CKThreadLocalComponentScope::currentScope()->stack.top().node.scopeHandle.globalIdentifier);
}
}
}
- (void)testComponentScopeHandleGlobalIdentifierIsNotTheSameBetweenSiblingsWithComponentScopeCollision
{
CKComponentScopeRoot *root = CKComponentScopeRootWithDefaultPredicates(nil, nil);
{
CKThreadLocalComponentScope threadScope(root, {});
int32_t globalIdentifier;
{
CKComponentScope scope([CKCompositeComponent class], @"macaque");
globalIdentifier = CKThreadLocalComponentScope::currentScope()->stack.top().node.scopeHandle.globalIdentifier;
}
{
CKComponentScope scope([CKCompositeComponent class], @"macaque");
XCTAssertNotEqual(globalIdentifier, CKThreadLocalComponentScope::currentScope()->stack.top().node.scopeHandle.globalIdentifier);
}
}
}
- (void)testComponentScopeHandleGlobalIdentifierIsNotTheSameBetweenDescendantsWithComponentScopeCollision
{
CKComponentScopeRoot *root = CKComponentScopeRootWithDefaultPredicates(nil, nil);
{
CKThreadLocalComponentScope threadScope(root, {});
int32_t globalIdentifier;
{
CKComponentScope scope1([CKCompositeComponent class], @"macaque");
{
CKComponentScope scope2([CKCompositeComponent class], @"moose");
globalIdentifier = CKThreadLocalComponentScope::currentScope()->stack.top().node.scopeHandle.globalIdentifier;
}
}
{
CKComponentScope scope1([CKCompositeComponent class], @"macaque");
{
CKComponentScope scope2([CKCompositeComponent class], @"moose");
XCTAssertNotEqual(globalIdentifier, CKThreadLocalComponentScope::currentScope()->stack.top().node.scopeHandle.globalIdentifier);
}
}
}
}
- (void)testComponentScopeHandleGlobalIdentifierIsTheSameBetweenDescendantsWithComponentScopeCollisionAcrossComponentScopeRoots
{
CKComponentScopeRoot *root1 = CKComponentScopeRootWithDefaultPredicates(nil, nil);
CKComponentScopeRoot *root2;
int32_t globalIdentifier;
{
CKThreadLocalComponentScope threadScope(root1, {});
{
CKComponentScope scope1([CKCompositeComponent class], @"macaque");
{
CKComponentScope scope2([CKCompositeComponent class], @"moose");
}
}
{
CKComponentScope scope1([CKCompositeComponent class], @"macaque");
{
CKComponentScope scope2([CKCompositeComponent class], @"moose");
globalIdentifier = CKThreadLocalComponentScope::currentScope()->stack.top().node.scopeHandle.globalIdentifier;
}
}
root2 = threadScope.newScopeRoot;
}
{
CKThreadLocalComponentScope threadScope(root2, {});
{
CKComponentScope scope1([CKCompositeComponent class], @"macaque");
{
CKComponentScope scope2([CKCompositeComponent class], @"moose");
}
}
{
CKComponentScope scope1([CKCompositeComponent class], @"macaque");
{
CKComponentScope scope2([CKCompositeComponent class], @"moose");
XCTAssertEqual(globalIdentifier, CKThreadLocalComponentScope::currentScope()->stack.top().node.scopeHandle.globalIdentifier);
}
}
}
}
static BOOL testComponentProtocolPredicate(id<CKComponentProtocol> component)
{
return [component conformsToProtocol:@protocol(TestScopedProtocol)];
}
- (void)testComponentScopeRootRegisteringProtocolComponentFindsThatComponentWhenEnumerating
{
CKComponentScopeRoot *root = [CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{&testComponentProtocolPredicate}
componentControllerPredicates:{}];
CKThreadLocalComponentScope threadScope(root, {});
TestComponentWithScopedProtocol *c = [TestComponentWithScopedProtocol new];
[root registerComponent:c];
__block BOOL foundComponent = NO;
[root
enumerateComponentsMatchingPredicate:&testComponentProtocolPredicate
block:^(id<CKComponentProtocol> component) {
if (c == component) {
foundComponent = YES;
}
}];
XCTAssert(foundComponent, @"Should have enumerated and found the input component");
}
- (void)testComponentScopeRootFactoryRegisteringProtocolComponentFindsThatComponentWhenEnumerating
{
CKComponentScopeRoot *root = CKComponentScopeRootWithPredicates(nil, nil, {&testComponentProtocolPredicate}, {});
CKThreadLocalComponentScope threadScope(root, {});
TestComponentWithScopedProtocol *c = [TestComponentWithScopedProtocol new];
[root registerComponent:c];
__block BOOL foundComponent = NO;
[root
enumerateComponentsMatchingPredicate:&testComponentProtocolPredicate
block:^(id<CKComponentProtocol> component) {
if (c == component) {
foundComponent = YES;
}
}];
XCTAssert(foundComponent, @"Should have enumerated and found the input component");
}
- (void)testComponentScopeRootRegisteringDuplicateProtocolComponent
{
CKComponentScopeRoot *root = [CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{&testComponentProtocolPredicate}
componentControllerPredicates:{}];
CKThreadLocalComponentScope threadScope(root, {});
TestComponentWithScopedProtocol *c = [TestComponentWithScopedProtocol new];
[root registerComponent:c];
[root registerComponent:c];
__block NSInteger numberOfComponents = 0;
[root
enumerateComponentsMatchingPredicate:&testComponentProtocolPredicate
block:^(id<CKComponentProtocol> component) {
++numberOfComponents;
}];
XCTAssert(numberOfComponents == 1, @"Should have deduplicate the component");
}
- (void)testComponentScopeRootFactoryRegisteringDuplicateProtocolComponent
{
CKComponentScopeRoot *root = CKComponentScopeRootWithPredicates(nil, nil, {&testComponentProtocolPredicate}, {});
CKThreadLocalComponentScope threadScope(root, {});
TestComponentWithScopedProtocol *c = [TestComponentWithScopedProtocol new];
[root registerComponent:c];
[root registerComponent:c];
__block NSInteger numberOfComponents = 0;
[root
enumerateComponentsMatchingPredicate:&testComponentProtocolPredicate
block:^(id<CKComponentProtocol> component) {
++numberOfComponents;
}];
XCTAssert(numberOfComponents == 1, @"Should have deduplicate the component");
}
- (void)testComponentScopeRootRegisteringMultipleProtocolComponentFindsBothComponentsWhenEnumerating
{
CKComponentScopeRoot *root = [CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{&testComponentProtocolPredicate}
componentControllerPredicates:{}];
CKThreadLocalComponentScope threadScope(root, {});
TestComponentWithScopedProtocol *c1 = [TestComponentWithScopedProtocol new];
[root registerComponent:c1];
TestComponentWithScopedProtocol *c2 = [TestComponentWithScopedProtocol new];
[root registerComponent:c2];
__block BOOL foundC1 = NO;
__block BOOL foundC2 = NO;
[root
enumerateComponentsMatchingPredicate:&testComponentProtocolPredicate
block:^(id<CKComponentProtocol> component) {
if (c1 == component) {
foundC1 = YES;
}
if (c2 == component) {
foundC2 = YES;
}
}];
XCTAssert(foundC1 && foundC2, @"Should have enumerated and found the input components");
}
- (void)testComponentScopeRootFactoryRegisteringMultipleProtocolComponentFindsBothComponentsWhenEnumerating
{
CKComponentScopeRoot *root = CKComponentScopeRootWithPredicates(nil, nil, {&testComponentProtocolPredicate}, {});
CKThreadLocalComponentScope threadScope(root, {});
TestComponentWithScopedProtocol *c1 = [TestComponentWithScopedProtocol new];
[root registerComponent:c1];
TestComponentWithScopedProtocol *c2 = [TestComponentWithScopedProtocol new];
[root registerComponent:c2];
__block BOOL foundC1 = NO;
__block BOOL foundC2 = NO;
[root
enumerateComponentsMatchingPredicate:&testComponentProtocolPredicate
block:^(id<CKComponentProtocol> component) {
if (c1 == component) {
foundC1 = YES;
}
if (c2 == component) {
foundC2 = YES;
}
}];
XCTAssert(foundC1 && foundC2, @"Should have enumerated and found the input components");
}
- (void)testComponentScopeRootRegisteringNonProtocolComponentFindsNoComponentsWhenEnumerating
{
CKComponentScopeRoot *root = [CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{&testComponentProtocolPredicate}
componentControllerPredicates:{}];
CKThreadLocalComponentScope threadScope(root, {});
TestComponentWithoutScopedProtocol *c = [TestComponentWithoutScopedProtocol new];
[root registerComponent:c];
[root
enumerateComponentsMatchingPredicate:&testComponentProtocolPredicate
block:^(id<CKComponentProtocol> component) {
XCTFail(@"Should not have found any components");
}];
}
- (void)testComponentScopeRootFactoryRegisteringNonProtocolComponentFindsNoComponentsWhenEnumerating
{
CKComponentScopeRoot *root = CKComponentScopeRootWithPredicates(nil, nil, {&testComponentProtocolPredicate}, {});
CKThreadLocalComponentScope threadScope(root, {});
TestComponentWithoutScopedProtocol *c = [TestComponentWithoutScopedProtocol new];
[root registerComponent:c];
[root
enumerateComponentsMatchingPredicate:&testComponentProtocolPredicate
block:^(id<CKComponentProtocol> component) {
XCTFail(@"Should not have found any components");
}];
}
static BOOL testComponentControllerProtocolPredicate(id<CKComponentControllerProtocol> component)
{
return [component conformsToProtocol:@protocol(TestScopedProtocol)];
}
- (void)testComponentScopeRootRegisteringProtocolComponentControllerFindsThatControllerWhenEnumerating
{
CKComponentScopeRoot *root = [CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{}
componentControllerPredicates:{&testComponentControllerProtocolPredicate}];
CKThreadLocalComponentScope threadScope(root, {});
TestComponentControllerWithScopedProtocol *c = [TestComponentControllerWithScopedProtocol new];
[root registerComponentController:c];
__block BOOL foundController = NO;
[root
enumerateComponentControllersMatchingPredicate:&testComponentControllerProtocolPredicate
block:^(id<CKComponentControllerProtocol> componentController) {
if (c == componentController) {
foundController = YES;
}
}];
XCTAssert(foundController, @"Should have enumerated and found the input controller");
}
- (void)testComponentScopeRootFactoryRegisteringProtocolComponentControllerFindsThatControllerWhenEnumerating
{
CKComponentScopeRoot *root = CKComponentScopeRootWithPredicates(nil, nil, {}, {&testComponentControllerProtocolPredicate});
CKThreadLocalComponentScope threadScope(root, {});
TestComponentControllerWithScopedProtocol *c = [TestComponentControllerWithScopedProtocol new];
[root registerComponentController:c];
__block BOOL foundController = NO;
[root
enumerateComponentControllersMatchingPredicate:&testComponentControllerProtocolPredicate
block:^(id<CKComponentControllerProtocol> componentController) {
if (c == componentController) {
foundController = YES;
}
}];
XCTAssert(foundController, @"Should have enumerated and found the input controller");
}
- (void)testComponentScopeRootRegisteringDuplicateProtocolComponentController
{
CKComponentScopeRoot *root = [CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{}
componentControllerPredicates:{&testComponentControllerProtocolPredicate}];
CKThreadLocalComponentScope threadScope(root, {});
TestComponentControllerWithScopedProtocol *c = [TestComponentControllerWithScopedProtocol new];
[root registerComponentController:c];
[root registerComponentController:c];
__block NSInteger numberOfComponentControllers = 0;
[root
enumerateComponentControllersMatchingPredicate:&testComponentControllerProtocolPredicate
block:^(id<CKComponentControllerProtocol> componentController) {
++numberOfComponentControllers;
}];
XCTAssert(numberOfComponentControllers == 1, @"Should have deduplicate the component controller");
}
- (void)testComponentScopeRootFactoryRegisteringDuplicateProtocolComponentController
{
CKComponentScopeRoot *root = CKComponentScopeRootWithPredicates(nil, nil, {}, {&testComponentControllerProtocolPredicate});
CKThreadLocalComponentScope threadScope(root, {});
TestComponentControllerWithScopedProtocol *c = [TestComponentControllerWithScopedProtocol new];
[root registerComponentController:c];
[root registerComponentController:c];
__block NSInteger numberOfComponentControllers = 0;
[root
enumerateComponentControllersMatchingPredicate:&testComponentControllerProtocolPredicate
block:^(id<CKComponentControllerProtocol> componentController) {
++numberOfComponentControllers;
}];
XCTAssert(numberOfComponentControllers == 1, @"Should have deduplicate the component controller");
}
- (void)testComponentScopeRootRegisteringMultipleProtocolComponentControllersFindsBothControllersWhenEnumerating
{
CKComponentScopeRoot *root = [CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{}
componentControllerPredicates:{&testComponentControllerProtocolPredicate}];
CKThreadLocalComponentScope threadScope(root, {});
TestComponentControllerWithScopedProtocol *c1 = [TestComponentControllerWithScopedProtocol new];
[root registerComponentController:c1];
TestComponentControllerWithScopedProtocol *c2 = [TestComponentControllerWithScopedProtocol new];
[root registerComponentController:c2];
__block BOOL foundC1 = NO;
__block BOOL foundC2 = NO;
[root
enumerateComponentControllersMatchingPredicate:&testComponentControllerProtocolPredicate
block:^(id<CKComponentControllerProtocol> componentController) {
if (c1 == componentController) {
foundC1 = YES;
}
if (c2 == componentController) {
foundC2 = YES;
}
}];
XCTAssert(foundC1 && foundC2, @"Should have enumerated and found the input controllers");
}
- (void)testComponentScopeRootFactoryRegisteringMultipleProtocolComponentControllersFindsBothControllersWhenEnumerating
{
CKComponentScopeRoot *root = CKComponentScopeRootWithPredicates(nil, nil, {}, {&testComponentControllerProtocolPredicate});
CKThreadLocalComponentScope threadScope(root, {});
TestComponentControllerWithScopedProtocol *c1 = [TestComponentControllerWithScopedProtocol new];
[root registerComponentController:c1];
TestComponentControllerWithScopedProtocol *c2 = [TestComponentControllerWithScopedProtocol new];
[root registerComponentController:c2];
__block BOOL foundC1 = NO;
__block BOOL foundC2 = NO;
[root
enumerateComponentControllersMatchingPredicate:&testComponentControllerProtocolPredicate
block:^(id<CKComponentControllerProtocol> componentController) {
if (c1 == componentController) {
foundC1 = YES;
}
if (c2 == componentController) {
foundC2 = YES;
}
}];
XCTAssert(foundC1 && foundC2, @"Should have enumerated and found the input controllers");
}
@end
@interface CKComponentScopeRootTests_RegistrationAndEnumeration: XCTestCase
@end
@implementation CKComponentScopeRootTests_RegistrationAndEnumeration
- (void)testComponentScopeRootRegisteringProtocolComponentFindsThatComponentWhenEnumerating
{
const auto root = makeScopeRootWithComponentPredicate(&testComponentProtocolPredicate);
const auto threadScope = CKThreadLocalComponentScope {root, {}};
const auto c = [TestComponentWithScopedProtocol new];
[root registerComponent:c];
XCTAssert(CK::Collection::contains([root componentsMatchingPredicate:&testComponentProtocolPredicate], c),
@"Should have enumerated and found the input component");
}
- (void)testComponentScopeRootFactoryRegisteringProtocolComponentFindsThatComponentWhenEnumerating
{
const auto root = CKComponentScopeRootWithPredicates(nil, nil, {&testComponentProtocolPredicate}, {});
const auto threadScope = CKThreadLocalComponentScope {root, {}};
const auto c = [TestComponentWithScopedProtocol new];
[root registerComponent:c];
XCTAssert(CK::Collection::contains([root componentsMatchingPredicate:&testComponentProtocolPredicate], c),
@"Should have enumerated and found the input component");
}
- (void)testComponentScopeRootRegisteringDuplicateProtocolComponent
{
const auto root = makeScopeRootWithComponentPredicate(&testComponentProtocolPredicate);
const auto threadScope = CKThreadLocalComponentScope {root, {}};
const auto c = [TestComponentWithScopedProtocol new];
[root registerComponent:c];
[root registerComponent:c];
XCTAssertEqual([root componentsMatchingPredicate:&testComponentProtocolPredicate].size(), 1, @"Should have deduplicate the component");
}
- (void)testComponentScopeRootFactoryRegisteringDuplicateProtocolComponent
{
const auto root = CKComponentScopeRootWithPredicates(nil, nil, {&testComponentProtocolPredicate}, {});
const auto threadScope = CKThreadLocalComponentScope {root, {}};
const auto c = [TestComponentWithScopedProtocol new];
[root registerComponent:c];
[root registerComponent:c];
XCTAssert(CK::Collection::contains([root componentsMatchingPredicate:&testComponentProtocolPredicate], c),
@"Should have enumerated and found the input component");
}
- (void)testComponentScopeRootRegisteringMultipleProtocolComponentFindsBothComponentsWhenEnumerating
{
const auto root = makeScopeRootWithComponentPredicate(&testComponentProtocolPredicate);
const auto threadScope = CKThreadLocalComponentScope {root, {}};
const auto c1 = [TestComponentWithScopedProtocol new];
const auto c2 = [TestComponentWithScopedProtocol new];
[root registerComponent:c1];
[root registerComponent:c2];
const auto foundC1 = CK::Collection::contains([root componentsMatchingPredicate:&testComponentProtocolPredicate], c1);
const auto foundC2 = CK::Collection::contains([root componentsMatchingPredicate:&testComponentProtocolPredicate], c2);
XCTAssert(foundC1 && foundC2, @"Should have enumerated and found the input components");
}
- (void)testComponentScopeRootFactoryRegisteringMultipleProtocolComponentFindsBothComponentsWhenEnumerating
{
const auto root = CKComponentScopeRootWithPredicates(nil, nil, {&testComponentProtocolPredicate}, {});
const auto threadScope = CKThreadLocalComponentScope {root, {}};
const auto c1 = [TestComponentWithScopedProtocol new];
const auto c2 = [TestComponentWithScopedProtocol new];
[root registerComponent:c1];
[root registerComponent:c2];
const auto foundC1 = CK::Collection::contains([root componentsMatchingPredicate:&testComponentProtocolPredicate], c1);
const auto foundC2 = CK::Collection::contains([root componentsMatchingPredicate:&testComponentProtocolPredicate], c2);
XCTAssert(foundC1 && foundC2, @"Should have enumerated and found the input components");
}
- (void)testComponentScopeRootRegisteringNonProtocolComponentFindsNoComponentsWhenEnumerating
{
const auto root = makeScopeRootWithComponentPredicate(&testComponentProtocolPredicate);
const auto threadScope = CKThreadLocalComponentScope {root, {}};
const auto c = [TestComponentWithoutScopedProtocol new];
[root registerComponent:c];
XCTAssert([root componentsMatchingPredicate:&testComponentProtocolPredicate].empty(), @"Should not have found any components");
}
- (void)testComponentScopeRootFactoryRegisteringNonProtocolComponentFindsNoComponentsWhenEnumerating
{
const auto root = CKComponentScopeRootWithPredicates(nil, nil, {&testComponentProtocolPredicate}, {});
const auto threadScope = CKThreadLocalComponentScope {root, {}};
const auto c = [TestComponentWithoutScopedProtocol new];
[root registerComponent:c];
XCTAssert([root componentsMatchingPredicate:&testComponentProtocolPredicate].empty(), @"Should not have found any components");
}
static auto makeScopeRootWithComponentPredicate(const CKComponentPredicate p) -> CKComponentScopeRoot *
{
return [CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{p}
componentControllerPredicates:{}];
}
@end