FIRComponentContainerTest.m 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. // Copyright 2018 Google
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "FirebaseCore/Tests/Unit/FIRTestCase.h"
  15. #import "FirebaseCore/Sources/FIRComponentContainerInternal.h"
  16. #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
  17. #import "FirebaseCore/Tests/Unit/FIRTestComponents.h"
  18. /// Internally exposed methods and properties for testing.
  19. @interface FIRComponentContainer (TestInternal)
  20. @property(nonatomic, strong) NSMutableDictionary<NSString *, FIRComponentCreationBlock> *components;
  21. @property(nonatomic, strong) NSMutableDictionary<NSString *, id> *cachedInstances;
  22. + (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass
  23. inSet:(NSMutableSet<Class> *)allRegistrants;
  24. - (instancetype)initWithApp:(FIRApp *)app registrants:(NSMutableSet<Class> *)allRegistrants;
  25. @end
  26. @interface FIRComponentContainer (TestInternalImplementations)
  27. - (instancetype)initWithApp:(FIRApp *)app
  28. components:(NSDictionary<NSString *, FIRComponentCreationBlock> *)components;
  29. @end
  30. @implementation FIRComponentContainer (TestInternalImplementations)
  31. - (instancetype)initWithApp:(FIRApp *)app
  32. components:(NSDictionary<NSString *, FIRComponentCreationBlock> *)components {
  33. self = [self initWithApp:app registrants:[[NSMutableSet alloc] init]];
  34. if (self) {
  35. self.components = [components mutableCopy];
  36. }
  37. return self;
  38. }
  39. @end
  40. @interface FIRComponentContainerTest : FIRTestCase {
  41. FIRApp *_hostApp;
  42. }
  43. @end
  44. @implementation FIRComponentContainerTest
  45. - (void)tearDown {
  46. _hostApp = nil;
  47. [super tearDown];
  48. }
  49. #pragma mark - Registration Tests
  50. - (void)testRegisteringConformingClass {
  51. NSMutableSet<Class> *allRegistrants = [NSMutableSet<Class> set];
  52. Class testClass = [FIRTestClass class];
  53. [FIRComponentContainer registerAsComponentRegistrant:testClass inSet:allRegistrants];
  54. XCTAssertTrue([allRegistrants containsObject:testClass]);
  55. }
  56. - (void)testComponentsPopulatedOnInit {
  57. FIRComponentContainer *container = [self containerWithRegistrants:@[ [FIRTestClass class] ]];
  58. // Verify that the block is stored.
  59. NSString *protocolName = NSStringFromProtocol(@protocol(FIRTestProtocol));
  60. FIRComponentCreationBlock creationBlock = container.components[protocolName];
  61. XCTAssertNotNil(creationBlock);
  62. }
  63. #pragma mark - Caching Tests
  64. - (void)testInstanceCached {
  65. FIRComponentContainer *container =
  66. [self containerWithRegistrants:@[ [FIRTestClassCached class] ]];
  67. // Fetch an instance for `FIRTestProtocolCached`, then fetch it again to assert it's cached.
  68. id<FIRTestProtocolCached> instance1 = FIR_COMPONENT(FIRTestProtocolCached, container);
  69. XCTAssertNotNil(instance1);
  70. id<FIRTestProtocolCached> instance2 = FIR_COMPONENT(FIRTestProtocolCached, container);
  71. XCTAssertNotNil(instance2);
  72. XCTAssertEqual(instance1, instance2);
  73. }
  74. - (void)testInstanceNotCached {
  75. FIRComponentContainer *container = [self containerWithRegistrants:@[ [FIRTestClass class] ]];
  76. // Retrieve an instance from the container, then fetch it again and ensure it's not the same
  77. // instance.
  78. id<FIRTestProtocol> instance1 = FIR_COMPONENT(FIRTestProtocol, container);
  79. XCTAssertNotNil(instance1);
  80. id<FIRTestProtocol> instance2 = FIR_COMPONENT(FIRTestProtocol, container);
  81. XCTAssertNotNil(instance2);
  82. XCTAssertNotEqual(instance1, instance2);
  83. }
  84. - (void)testRemoveAllCachedInstances {
  85. FIRComponentContainer *container = [self containerWithRegistrants:@[
  86. [FIRTestClass class], [FIRTestClassCached class], [FIRTestClassEagerCached class],
  87. [FIRTestClassCachedWithDep class]
  88. ]];
  89. // Retrieve an instance of FIRTestClassCached to ensure it's cached.
  90. id<FIRTestProtocolCached> cachedInstance1 = FIR_COMPONENT(FIRTestProtocolCached, container);
  91. id<FIRTestProtocolEagerCached> eagerInstance1 =
  92. FIR_COMPONENT(FIRTestProtocolEagerCached, container);
  93. // FIRTestClassEagerCached and FIRTestClassCached instances should be cached at this point.
  94. XCTAssertTrue(container.cachedInstances.count == 2);
  95. // Remove the instances and verify cachedInstances is empty, and that new instances returned from
  96. // the container don't match the old ones.
  97. [container removeAllCachedInstances];
  98. XCTAssertTrue(container.cachedInstances.count == 0);
  99. id<FIRTestProtocolCached> cachedInstance2 = FIR_COMPONENT(FIRTestProtocolCached, container);
  100. XCTAssertNotEqual(cachedInstance1, cachedInstance2);
  101. id<FIRTestProtocolEagerCached> eagerInstance2 =
  102. FIR_COMPONENT(FIRTestProtocolEagerCached, container);
  103. XCTAssertNotEqual(eagerInstance1, eagerInstance2);
  104. }
  105. - (void)testRemoveAllComponents {
  106. FIRComponentContainer *container = [self containerWithRegistrants:@[
  107. [FIRTestClass class], [FIRTestClassCached class], [FIRTestClassEagerCached class],
  108. [FIRTestClassCachedWithDep class]
  109. ]];
  110. // Retrieve an instance of FIRTestClassCached to ensure it's cached.
  111. id<FIRTestProtocolCached> cachedInstance1 = FIR_COMPONENT(FIRTestProtocolCached, container);
  112. XCTAssertNotNil(cachedInstance1);
  113. id<FIRTestProtocolEagerCached> eagerInstance1 =
  114. FIR_COMPONENT(FIRTestProtocolEagerCached, container);
  115. XCTAssertNotNil(eagerInstance1);
  116. // FIRTestClassEagerCached and FIRTestClassCached instances should be cached at this point.
  117. XCTAssertTrue(container.cachedInstances.count == 2);
  118. // Remove all components.
  119. [container removeAllComponents];
  120. // Remove the instances.
  121. [container removeAllCachedInstances];
  122. // Verify that no new instances are created.
  123. id<FIRTestProtocolCached> cachedInstance2 = FIR_COMPONENT(FIRTestProtocolCached, container);
  124. XCTAssertNil(cachedInstance2);
  125. id<FIRTestProtocolEagerCached> eagerInstance2 =
  126. FIR_COMPONENT(FIRTestProtocolEagerCached, container);
  127. XCTAssertNil(eagerInstance2);
  128. }
  129. #pragma mark - Instantiation Tests
  130. - (void)testEagerInstantiation {
  131. // Create a container with `FIRTestClassEagerCached` as a registrant, which provides the
  132. // implementation for `FIRTestProtocolEagerCached` and requires eager instantiation as well as
  133. // caching so the test can verify it was eagerly instantiated.
  134. FIRComponentContainer *container =
  135. [self containerWithRegistrants:@[ [FIRTestClassEagerCached class] ]];
  136. NSString *protocolName = NSStringFromProtocol(@protocol(FIRTestProtocolEagerCached));
  137. XCTAssertNotNil(container.cachedInstances[protocolName]);
  138. }
  139. #pragma mark - Input Validation Tests
  140. - (void)testProtocolAlreadyRegistered {
  141. // Register two classes that provide the same protocol. Only one should be stored, and there
  142. // should be a log stating that the protocol has already been registered. Right now there's no
  143. // guarantee which one will be registered first since it's an NSSet under the hood, but that could
  144. // change in the future.
  145. // TODO(wilsonryan): Assert that the log gets called warning that it's already been registered.
  146. FIRComponentContainer *container =
  147. [self containerWithRegistrants:@[ [FIRTestClass class], [FIRTestClassDuplicate class] ]];
  148. XCTAssert(container.components.count == 1);
  149. }
  150. #pragma mark - Dependency Tests
  151. - (void)testDependencyDoesntBlock {
  152. /// Test a class that has a dependency, and fetching doesn't block the internal queue.
  153. FIRComponentContainer *container = [self
  154. containerWithRegistrants:@[ [FIRTestClassCached class], [FIRTestClassCachedWithDep class] ]];
  155. XCTAssert(container.components.count == 2);
  156. id<FIRTestProtocolCachedWithDep> instanceWithDep =
  157. FIR_COMPONENT(FIRTestProtocolCachedWithDep, container);
  158. XCTAssertNotNil(instanceWithDep);
  159. }
  160. - (void)testDependencyRemoveAllCachedInstancesDoesntBlock {
  161. /// Test a class that has a dependency, and fetching doesn't block the internal queue.
  162. FIRComponentContainer *container = [self
  163. containerWithRegistrants:@[ [FIRTestClassCached class], [FIRTestClassCachedWithDep class] ]];
  164. XCTAssert(container.components.count == 2);
  165. id<FIRTestProtocolCachedWithDep> instanceWithDep =
  166. FIR_COMPONENT(FIRTestProtocolCachedWithDep, container);
  167. XCTAssertNotNil(instanceWithDep);
  168. XCTAssertNotNil(instanceWithDep.testProperty);
  169. // Both `instanceWithDep` and `testProperty` should be cached now.
  170. XCTAssertTrue(container.cachedInstances.count == 2);
  171. // Remove the instances and verify cachedInstances is empty, and doesn't block the queue.
  172. [container removeAllCachedInstances];
  173. XCTAssertTrue(container.cachedInstances.count == 0);
  174. }
  175. #pragma mark - Convenience Methods
  176. /// Create a container that has registered the test class.
  177. - (FIRComponentContainer *)containerWithRegistrants:(NSArray<Class> *)registrants {
  178. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  179. GCMSenderID:kGCMSenderID];
  180. _hostApp = [[FIRApp alloc] initInstanceWithName:@"fake_app" options:options];
  181. NSMutableSet<Class> *allRegistrants = [NSMutableSet<Class> set];
  182. // Initialize the container with the test classes.
  183. for (Class c in registrants) {
  184. [FIRComponentContainer registerAsComponentRegistrant:c inSet:allRegistrants];
  185. }
  186. // Override the app's container with the newly instantiated container.
  187. FIRComponentContainer *container = [[FIRComponentContainer alloc] initWithApp:_hostApp
  188. registrants:allRegistrants];
  189. _hostApp.container = container;
  190. // Instantiate all the components that were eagerly registered now that all other properties are
  191. // configured.
  192. [container instantiateEagerComponents];
  193. return container;
  194. }
  195. @end