FIRComponentContainerTest.m 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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 "FIRTestCase.h"
  15. #import <FirebaseCore/FIRAppInternal.h>
  16. #import <FirebaseCore/FIRComponent.h>
  17. #import <FirebaseCore/FIRComponentContainerInternal.h>
  18. #import <FirebaseCore/FIROptions.h>
  19. #import "FIRTestComponents.h"
  20. /// Internally exposed methods and properties for testing.
  21. @interface FIRComponentContainer (TestInternal)
  22. @property(nonatomic, strong) NSMutableDictionary<NSString *, FIRComponentCreationBlock> *components;
  23. @property(nonatomic, strong) NSMutableDictionary<NSString *, id> *cachedInstances;
  24. + (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass
  25. inSet:(NSMutableSet<Class> *)allRegistrants;
  26. - (instancetype)initWithApp:(FIRApp *)app registrants:(NSMutableSet<Class> *)allRegistrants;
  27. @end
  28. @interface FIRComponentContainer (TestInternalImplementations)
  29. - (instancetype)initWithApp:(FIRApp *)app
  30. components:(NSDictionary<NSString *, FIRComponentCreationBlock> *)components;
  31. @end
  32. @implementation FIRComponentContainer (TestInternalImplementations)
  33. - (instancetype)initWithApp:(FIRApp *)app
  34. components:(NSDictionary<NSString *, FIRComponentCreationBlock> *)components {
  35. self = [self initWithApp:app registrants:[[NSMutableSet alloc] init]];
  36. if (self) {
  37. self.components = [components mutableCopy];
  38. }
  39. return self;
  40. }
  41. @end
  42. @interface FIRComponentContainerTest : FIRTestCase {
  43. FIRApp *_hostApp;
  44. }
  45. @end
  46. @implementation FIRComponentContainerTest
  47. - (void)tearDown {
  48. _hostApp = nil;
  49. [super tearDown];
  50. }
  51. #pragma mark - Registration Tests
  52. - (void)testRegisteringConformingClass {
  53. NSMutableSet<Class> *allRegistrants = [NSMutableSet<Class> set];
  54. Class testClass = [FIRTestClass class];
  55. [FIRComponentContainer registerAsComponentRegistrant:testClass inSet:allRegistrants];
  56. XCTAssertTrue([allRegistrants containsObject:testClass]);
  57. }
  58. - (void)testComponentsPopulatedOnInit {
  59. FIRComponentContainer *container = [self containerWithRegistrants:@ [[FIRTestClass class]]];
  60. // Verify that the block is stored.
  61. NSString *protocolName = NSStringFromProtocol(@protocol(FIRTestProtocol));
  62. FIRComponentCreationBlock creationBlock = container.components[protocolName];
  63. OCMExpect(creationBlock);
  64. }
  65. #pragma mark - Caching Tests
  66. - (void)testInstanceCached {
  67. FIRComponentContainer *container = [self containerWithRegistrants:@ [[FIRTestClassCached class]]];
  68. // Fetch an instance for `FIRTestProtocolCached`, then fetch it again to assert it's cached.
  69. id<FIRTestProtocolCached> instance1 = FIR_COMPONENT(FIRTestProtocolCached, container);
  70. XCTAssertNotNil(instance1);
  71. id<FIRTestProtocolCached> instance2 = FIR_COMPONENT(FIRTestProtocolCached, container);
  72. XCTAssertNotNil(instance2);
  73. XCTAssertEqual(instance1, instance2);
  74. }
  75. - (void)testInstanceNotCached {
  76. FIRComponentContainer *container = [self containerWithRegistrants:@ [[FIRTestClass class]]];
  77. // Retrieve an instance from the container, then fetch it again and ensure it's not the same
  78. // instance.
  79. id<FIRTestProtocol> instance1 = FIR_COMPONENT(FIRTestProtocol, container);
  80. XCTAssertNotNil(instance1);
  81. id<FIRTestProtocol> instance2 = FIR_COMPONENT(FIRTestProtocol, container);
  82. XCTAssertNotNil(instance2);
  83. XCTAssertNotEqual(instance1, instance2);
  84. }
  85. - (void)testRemoveAllCachedInstances {
  86. FIRComponentContainer *container =
  87. [self containerWithRegistrants:@ [[FIRTestClass class], [FIRTestClassCached class],
  88. [FIRTestClassEagerCached class],
  89. [FIRTestClassCachedWithDep class]]];
  90. // Retrieve an instance of FIRTestClassCached to ensure it's cached.
  91. id<FIRTestProtocolCached> cachedInstance1 = FIR_COMPONENT(FIRTestProtocolCached, container);
  92. id<FIRTestProtocolEagerCached> eagerInstance1 =
  93. FIR_COMPONENT(FIRTestProtocolEagerCached, container);
  94. // FIRTestClassEagerCached and FIRTestClassCached instances should be cached at this point.
  95. XCTAssertTrue(container.cachedInstances.count == 2);
  96. // Remove the instances and verify cachedInstances is empty, and that new instances returned from
  97. // the container don't match the old ones.
  98. [container removeAllCachedInstances];
  99. XCTAssertTrue(container.cachedInstances.count == 0);
  100. id<FIRTestProtocolCached> cachedInstance2 = FIR_COMPONENT(FIRTestProtocolCached, container);
  101. XCTAssertNotEqual(cachedInstance1, cachedInstance2);
  102. id<FIRTestProtocolEagerCached> eagerInstance2 =
  103. FIR_COMPONENT(FIRTestProtocolEagerCached, container);
  104. XCTAssertNotEqual(eagerInstance1, eagerInstance2);
  105. }
  106. #pragma mark - Instantiation Tests
  107. - (void)testEagerInstantiation {
  108. // Create a container with `FIRTestClassEagerCached` as a registrant, which provides the
  109. // implementation for `FIRTestProtocolEagerCached` and requires eager instantiation as well as
  110. // caching so the test can verify it was eagerly instantiated.
  111. FIRComponentContainer *container =
  112. [self containerWithRegistrants:@ [[FIRTestClassEagerCached class]]];
  113. NSString *protocolName = NSStringFromProtocol(@protocol(FIRTestProtocolEagerCached));
  114. XCTAssertNotNil(container.cachedInstances[protocolName]);
  115. }
  116. #pragma mark - Input Validation Tests
  117. - (void)testProtocolAlreadyRegistered {
  118. // Register two classes that provide the same protocol. Only one should be stored, and there
  119. // should be a log stating that the protocol has already been registered. Right now there's no
  120. // guarantee which one will be registered first since it's an NSSet under the hood, but that could
  121. // change in the future.
  122. // TODO(wilsonryan): Assert that the log gets called warning that it's already been registered.
  123. FIRComponentContainer *container =
  124. [self containerWithRegistrants:@ [[FIRTestClass class], [FIRTestClassDuplicate class]]];
  125. XCTAssert(container.components.count == 1);
  126. }
  127. #pragma mark - Dependency Tests
  128. - (void)testDependencyDoesntBlock {
  129. /// Test a class that has a dependency, and fetching doesn't block the internal queue.
  130. FIRComponentContainer *container = [self
  131. containerWithRegistrants:@ [[FIRTestClassCached class], [FIRTestClassCachedWithDep class]]];
  132. XCTAssert(container.components.count == 2);
  133. id<FIRTestProtocolCachedWithDep> instanceWithDep =
  134. FIR_COMPONENT(FIRTestProtocolCachedWithDep, container);
  135. XCTAssertNotNil(instanceWithDep);
  136. }
  137. - (void)testDependencyRemoveAllCachedInstancesDoesntBlock {
  138. /// Test a class that has a dependency, and fetching doesn't block the internal queue.
  139. FIRComponentContainer *container = [self
  140. containerWithRegistrants:@ [[FIRTestClassCached class], [FIRTestClassCachedWithDep class]]];
  141. XCTAssert(container.components.count == 2);
  142. id<FIRTestProtocolCachedWithDep> instanceWithDep =
  143. FIR_COMPONENT(FIRTestProtocolCachedWithDep, container);
  144. XCTAssertNotNil(instanceWithDep);
  145. XCTAssertNotNil(instanceWithDep.testProperty);
  146. // Both `instanceWithDep` and `testProperty` should be cached now.
  147. XCTAssertTrue(container.cachedInstances.count == 2);
  148. // Remove the instances and verify cachedInstances is empty, and doesn't block the queue.
  149. [container removeAllCachedInstances];
  150. XCTAssertTrue(container.cachedInstances.count == 0);
  151. }
  152. #pragma mark - Convenience Methods
  153. /// Create a container that has registered the test class.
  154. - (FIRComponentContainer *)containerWithRegistrants:(NSArray<Class> *)registrants {
  155. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  156. GCMSenderID:kGCMSenderID];
  157. _hostApp = [[FIRApp alloc] initInstanceWithName:@"fake_app" options:options];
  158. NSMutableSet<Class> *allRegistrants = [NSMutableSet<Class> set];
  159. // Initialize the container with the test classes.
  160. for (Class c in registrants) {
  161. [FIRComponentContainer registerAsComponentRegistrant:c inSet:allRegistrants];
  162. }
  163. // Override the app's container with the newly instantiated container.
  164. FIRComponentContainer *container = [[FIRComponentContainer alloc] initWithApp:_hostApp
  165. registrants:allRegistrants];
  166. _hostApp.container = container;
  167. // Instantiate all the components that were eagerly registered now that all other properties are
  168. // configured.
  169. [container instantiateEagerComponents];
  170. return container;
  171. }
  172. @end