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