Skip to content

Commit b345fe7

Browse files
authored
Add ComponentContainer tests for dependencies. (#3774)
* Add tests for container dependencies. * Add Component Container tests for dependencies. This ensures that no deadlocking occurs when fetching from the container, and when a delete occurs.
1 parent f750f87 commit b345fe7

File tree

3 files changed

+116
-10
lines changed

3 files changed

+116
-10
lines changed

Example/Core/Tests/FIRComponentContainerTest.m

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#import <FirebaseCore/FIRAppInternal.h>
1818
#import <FirebaseCore/FIRComponent.h>
1919
#import <FirebaseCore/FIRComponentContainerInternal.h>
20+
#import <FirebaseCore/FIROptions.h>
2021

2122
#import "FIRTestComponents.h"
2223

@@ -49,12 +50,19 @@ - (instancetype)initWithApp:(FIRApp *)app
4950

5051
@end
5152

52-
@interface FIRComponentContainerTest : FIRTestCase
53+
@interface FIRComponentContainerTest : FIRTestCase {
54+
FIRApp *_hostApp;
55+
}
5356

5457
@end
5558

5659
@implementation FIRComponentContainerTest
5760

61+
- (void)tearDown {
62+
_hostApp = nil;
63+
[super tearDown];
64+
}
65+
5866
#pragma mark - Registration Tests
5967

6068
- (void)testRegisteringConformingClass {
@@ -101,7 +109,8 @@ - (void)testInstanceNotCached {
101109
- (void)testRemoveAllCachedInstances {
102110
FIRComponentContainer *container =
103111
[self containerWithRegistrants:@ [[FIRTestClass class], [FIRTestClassCached class],
104-
[FIRTestClassEagerCached class]]];
112+
[FIRTestClassEagerCached class],
113+
[FIRTestClassCachedWithDep class]]];
105114

106115
// Retrieve an instance of FIRTestClassCached to ensure it's cached.
107116
id<FIRTestProtocolCached> cachedInstance1 = FIR_COMPONENT(FIRTestProtocolCached, container);
@@ -148,18 +157,57 @@ - (void)testProtocolAlreadyRegistered {
148157
XCTAssert(container.components.count == 1);
149158
}
150159

160+
#pragma mark - Dependency Tests
161+
162+
- (void)testDependencyDoesntBlock {
163+
/// Test a class that has a dependency, and fetching doesn't block the internal queue.
164+
FIRComponentContainer *container = [self
165+
containerWithRegistrants:@ [[FIRTestClassCached class], [FIRTestClassCachedWithDep class]]];
166+
XCTAssert(container.components.count == 2);
167+
168+
id<FIRTestProtocolCachedWithDep> instanceWithDep =
169+
FIR_COMPONENT(FIRTestProtocolCachedWithDep, container);
170+
XCTAssertNotNil(instanceWithDep);
171+
}
172+
173+
- (void)testDependencyRemoveAllCachedInstancesDoesntBlock {
174+
/// Test a class that has a dependency, and fetching doesn't block the internal queue.
175+
FIRComponentContainer *container = [self
176+
containerWithRegistrants:@ [[FIRTestClassCached class], [FIRTestClassCachedWithDep class]]];
177+
XCTAssert(container.components.count == 2);
178+
179+
id<FIRTestProtocolCachedWithDep> instanceWithDep =
180+
FIR_COMPONENT(FIRTestProtocolCachedWithDep, container);
181+
XCTAssertNotNil(instanceWithDep);
182+
XCTAssertNotNil(instanceWithDep.testProperty);
183+
184+
// Both `instanceWithDep` and `testProperty` should be cached now.
185+
XCTAssertTrue(container.cachedInstances.count == 2);
186+
187+
// Remove the instances and verify cachedInstances is empty, and doesn't block the queue.
188+
[container removeAllCachedInstances];
189+
XCTAssertTrue(container.cachedInstances.count == 0);
190+
}
191+
151192
#pragma mark - Convenience Methods
152193

153194
/// Create a container that has registered the test class.
154195
- (FIRComponentContainer *)containerWithRegistrants:(NSArray<Class> *)registrants {
155-
id appMock = OCMClassMock([FIRApp class]);
196+
FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
197+
GCMSenderID:kGCMSenderID];
198+
_hostApp = [[FIRApp alloc] initInstanceWithName:@"fake_app" options:options];
156199
NSMutableSet<Class> *allRegistrants = [NSMutableSet<Class> set];
157200

158201
// Initialize the container with the test classes.
159202
for (Class c in registrants) {
160203
[FIRComponentContainer registerAsComponentRegistrant:c inSet:allRegistrants];
161204
}
162-
return [[FIRComponentContainer alloc] initWithApp:appMock registrants:allRegistrants];
205+
206+
// Override the app's container with the newly instantiated container.
207+
FIRComponentContainer *container = [[FIRComponentContainer alloc] initWithApp:_hostApp
208+
registrants:allRegistrants];
209+
_hostApp.container = container;
210+
return container;
163211
}
164212

165213
@end

Example/Core/Tests/FIRTestComponents.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,33 @@
4646
/// A test class that is a component registrant that provides a component requiring eager
4747
/// instantiation, and is cached for easier validation that it was instantiated.
4848
@interface FIRTestClassEagerCached
49-
: NSObject <FIRTestProtocol, FIRComponentLifecycleMaintainer, FIRLibrary>
49+
: NSObject <FIRTestProtocolEagerCached, FIRComponentLifecycleMaintainer, FIRLibrary>
5050
@end
5151

5252
#pragma mark - Cached Component
5353

5454
/// A test protocol to be used for container testing.
5555
@protocol FIRTestProtocolCached
56+
- (void)cacheCow;
5657
@end
5758

5859
/// A test class that is a component registrant that provides a component that requests to be
5960
/// cached.
6061
@interface FIRTestClassCached
61-
: NSObject <FIRTestProtocol, FIRComponentLifecycleMaintainer, FIRLibrary>
62+
: NSObject <FIRTestProtocolCached, FIRComponentLifecycleMaintainer, FIRLibrary>
63+
@end
64+
65+
#pragma mark - Dependency on Standard
66+
67+
/// A test protocol to be used for container testing.
68+
@protocol FIRTestProtocolCachedWithDep
69+
@property(nonatomic, strong) id<FIRTestProtocolCached> testProperty;
70+
@end
71+
72+
/// A test class that is a component registrant that provides a component with a dependency on
73+
// `FIRTestProtocolCached`.
74+
@interface FIRTestClassCachedWithDep
75+
: NSObject <FIRTestProtocolCachedWithDep, FIRComponentLifecycleMaintainer, FIRLibrary>
76+
@property(nonatomic, strong) id<FIRTestProtocolCached> testProperty;
77+
- (instancetype)initWithTest:(id<FIRTestProtocolCached>)testInstance;
6278
@end

Example/Core/Tests/FIRTestComponents.m

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414

1515
#import "FIRTestComponents.h"
1616

17+
#import <FirebaseCore/FIRAppInternal.h>
1718
#import <FirebaseCore/FIRComponent.h>
19+
#import <FirebaseCore/FIRDependency.h>
1820

1921
#pragma mark - Standard Component
2022

@@ -92,9 +94,6 @@ - (void)doSomethingFaster {
9294
- (void)appWillBeDeleted:(FIRApp *)app {
9395
}
9496

95-
- (void)doSomething {
96-
}
97-
9897
@end
9998

10099
#pragma mark - Cached Component
@@ -118,7 +117,50 @@ @implementation FIRTestClassCached
118117
- (void)appWillBeDeleted:(FIRApp *)app {
119118
}
120119

121-
- (void)doSomething {
120+
/// FIRTestProtocolCached conformance.
121+
- (void)cacheCow {
122+
}
123+
124+
@end
125+
126+
#pragma mark - Test Component with Dependency
127+
128+
@implementation FIRTestClassCachedWithDep
129+
130+
- (instancetype)initWithTest:(id<FIRTestProtocolCached>)testInstance {
131+
self = [super init];
132+
if (self != nil) {
133+
self.testProperty = testInstance;
134+
}
135+
return self;
136+
}
137+
138+
- (void)appWillBeDeleted:(nonnull FIRApp *)app {
139+
// Do something that depends on the instance from our dependency.
140+
[self.testProperty cacheCow];
141+
142+
// Fetch from the container in the deletion function.
143+
id<FIRTestProtocolCached> anotherInstance = FIR_COMPONENT(FIRTestProtocolCached, app.container);
144+
[anotherInstance cacheCow];
145+
}
146+
147+
+ (nonnull NSArray<FIRComponent *> *)componentsToRegister {
148+
FIRDependency *dep = [FIRDependency dependencyWithProtocol:@protocol(FIRTestProtocolCached)];
149+
FIRComponent *testComponent = [FIRComponent
150+
componentWithProtocol:@protocol(FIRTestProtocolCachedWithDep)
151+
instantiationTiming:FIRInstantiationTimingLazy
152+
dependencies:@[ dep ]
153+
creationBlock:^id _Nullable(FIRComponentContainer *_Nonnull container,
154+
BOOL *_Nonnull isCacheable) {
155+
// Fetch from the container in the instantiation block.
156+
*isCacheable = YES;
157+
158+
id<FIRTestProtocolCached> test = FIR_COMPONENT(FIRTestProtocolCached, container);
159+
FIRTestClassCachedWithDep *instance =
160+
[[FIRTestClassCachedWithDep alloc] initWithTest:test];
161+
return instance;
162+
}];
163+
return @[ testComponent ];
122164
}
123165

124166
@end

0 commit comments

Comments
 (0)