FIRHeartbeatLoggerTests.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // Copyright 2021 Google LLC
  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 FirebaseCoreInternal;
  16. #import "FirebaseCore/Extension/FIRHeartbeatLogger.h"
  17. @interface FIRHeartbeatLogger (Internal)
  18. - (instancetype)initWithAppID:(NSString *)appID
  19. userAgentProvider:(NSString * (^)(void))userAgentProvider;
  20. @end
  21. @interface FIRHeartbeatLoggerTests : XCTestCase
  22. @property(nonatomic) FIRHeartbeatLogger *heartbeatLogger;
  23. @end
  24. @implementation FIRHeartbeatLoggerTests
  25. + (NSString *)dummyAppID {
  26. return NSStringFromClass([self class]);
  27. }
  28. + (NSString * (^)(void))dummyUserAgentProvider {
  29. return ^NSString * {
  30. return @"dummy_agent";
  31. };
  32. }
  33. + (NSString *)formattedStringForDate:(NSDate *)date {
  34. return [[FIRHeartbeatLoggingTestUtils dateFormatter] stringFromDate:date];
  35. }
  36. - (void)setUp {
  37. _heartbeatLogger =
  38. [[FIRHeartbeatLogger alloc] initWithAppID:[[self class] dummyAppID]
  39. userAgentProvider:[[self class] dummyUserAgentProvider]];
  40. [FIRHeartbeatLoggingTestUtils removeUnderlyingHeartbeatStorageContainersAndReturnError:nil];
  41. }
  42. - (void)tearDown {
  43. [FIRHeartbeatLoggingTestUtils removeUnderlyingHeartbeatStorageContainersAndReturnError:nil];
  44. }
  45. - (void)testDoNotLogMoreThanOnceToday {
  46. // Given
  47. FIRHeartbeatLogger *heartbeatLogger = self.heartbeatLogger;
  48. NSString *expectedDate = [[self class] formattedStringForDate:[NSDate date]];
  49. // When
  50. [heartbeatLogger log];
  51. [heartbeatLogger log];
  52. // Then
  53. FIRHeartbeatsPayload *heartbeatsPayload = [heartbeatLogger flushHeartbeatsIntoPayload];
  54. [self assertEncodedPayloadHeader:FIRHeaderValueFromHeartbeatsPayload(heartbeatsPayload)
  55. isEqualToPayloadJSON:@{
  56. @"version" : @2,
  57. @"heartbeats" : @[ @{@"agent" : @"dummy_agent", @"dates" : @[ expectedDate ]} ]
  58. }];
  59. }
  60. - (void)testDoNotLogMoreThanOnceToday_AfterFlushing {
  61. // Given
  62. FIRHeartbeatLogger *heartbeatLogger = self.heartbeatLogger;
  63. NSString *expectedDate = [[self class] formattedStringForDate:[NSDate date]];
  64. // When
  65. [heartbeatLogger log];
  66. FIRHeartbeatsPayload *firstHeartbeatsPayload = [heartbeatLogger flushHeartbeatsIntoPayload];
  67. [heartbeatLogger log];
  68. FIRHeartbeatsPayload *secondHeartbeatsPayload = [heartbeatLogger flushHeartbeatsIntoPayload];
  69. // Then
  70. [self assertEncodedPayloadHeader:FIRHeaderValueFromHeartbeatsPayload(firstHeartbeatsPayload)
  71. isEqualToPayloadJSON:@{
  72. @"version" : @2,
  73. @"heartbeats" : @[ @{@"agent" : @"dummy_agent", @"dates" : @[ expectedDate ]} ]
  74. }];
  75. [self assertHeartbeatsPayloadIsEmpty:secondHeartbeatsPayload];
  76. }
  77. - (void)testFlushing_UsingV1API_WhenHeartbeatsAreStored_ReturnsFIRDailyHeartbeatCodeSome {
  78. // Given
  79. FIRHeartbeatLogger *heartbeatLogger = self.heartbeatLogger;
  80. // When
  81. [heartbeatLogger log];
  82. FIRDailyHeartbeatCode heartbeatInfoCode = [heartbeatLogger heartbeatCodeForToday];
  83. // Then
  84. XCTAssertEqual(heartbeatInfoCode, FIRDailyHeartbeatCodeSome);
  85. }
  86. - (void)testFlushing_UsingV1API_WhenNoHeartbeatsAreStored_ReturnsFIRDailyHeartbeatCodeNone {
  87. // Given
  88. FIRHeartbeatLogger *heartbeatLogger = self.heartbeatLogger;
  89. // When
  90. FIRDailyHeartbeatCode heartbeatInfoCode = [heartbeatLogger heartbeatCodeForToday];
  91. // Then
  92. XCTAssertEqual(heartbeatInfoCode, FIRDailyHeartbeatCodeNone);
  93. }
  94. - (void)testFlushing_UsingV2API_WhenHeartbeatsAreStored_ReturnsNonEmptyPayload {
  95. // Given
  96. FIRHeartbeatLogger *heartbeatLogger = self.heartbeatLogger;
  97. NSString *expectedDate = [[self class] formattedStringForDate:[NSDate date]];
  98. // When
  99. [heartbeatLogger log];
  100. FIRHeartbeatsPayload *heartbeatsPayload = [heartbeatLogger flushHeartbeatsIntoPayload];
  101. // Then
  102. [self assertEncodedPayloadHeader:FIRHeaderValueFromHeartbeatsPayload(heartbeatsPayload)
  103. isEqualToPayloadJSON:@{
  104. @"version" : @2,
  105. @"heartbeats" : @[ @{@"agent" : @"dummy_agent", @"dates" : @[ expectedDate ]} ]
  106. }];
  107. }
  108. - (void)testFlushingAsync_UsingV2API_WhenHeartbeatsAreStored_ReturnsNonEmptyPayload API_AVAILABLE(
  109. ios(13.0), macosx(10.15), macCatalyst(13.0), tvos(13.0), watchos(6.0)) {
  110. // Given
  111. FIRHeartbeatLogger *heartbeatLogger = self.heartbeatLogger;
  112. NSString *expectedDate = [[self class] formattedStringForDate:[NSDate date]];
  113. // When
  114. [heartbeatLogger log];
  115. XCTestExpectation *expectation = [self expectationWithDescription:@"async flush"];
  116. [heartbeatLogger
  117. flushHeartbeatsIntoPayloadWithCompletionHandler:^(FIRHeartbeatsPayload *heartbeatsPayload) {
  118. // Then
  119. [self assertEncodedPayloadHeader:FIRHeaderValueFromHeartbeatsPayload(heartbeatsPayload)
  120. isEqualToPayloadJSON:@{
  121. @"version" : @2,
  122. @"heartbeats" : @[
  123. @{@"agent" : @"dummy_agent",
  124. @"dates" : @[ expectedDate ]}
  125. ]
  126. }];
  127. [expectation fulfill];
  128. }];
  129. [self waitForExpectations:@[ expectation ] timeout:1.0];
  130. }
  131. - (void)testFlushing_UsingV2API_WhenNoHeartbeatsAreStored_ReturnsEmptyPayload {
  132. // Given
  133. FIRHeartbeatLogger *heartbeatLogger = self.heartbeatLogger;
  134. // When
  135. FIRHeartbeatsPayload *heartbeatsPayload = [heartbeatLogger flushHeartbeatsIntoPayload];
  136. // Then
  137. [self assertHeartbeatsPayloadIsEmpty:heartbeatsPayload];
  138. }
  139. - (void)testFlushingAsync_UsingV2API_WhenNoHeartbeatsAreStored_ReturnsEmptyPayload API_AVAILABLE(
  140. ios(13.0), macosx(10.15), macCatalyst(13.0), tvos(13.0), watchos(6.0)) {
  141. // Given
  142. FIRHeartbeatLogger *heartbeatLogger = self.heartbeatLogger;
  143. // When
  144. XCTestExpectation *expectation = [self expectationWithDescription:@"async flush"];
  145. [heartbeatLogger
  146. flushHeartbeatsIntoPayloadWithCompletionHandler:^(FIRHeartbeatsPayload *heartbeatsPayload) {
  147. // Then
  148. [self assertHeartbeatsPayloadIsEmpty:heartbeatsPayload];
  149. [expectation fulfill];
  150. }];
  151. [self waitForExpectations:@[ expectation ] timeout:1.0];
  152. }
  153. - (void)testLogAndFlushUsingV1API_AndThenFlushAgainUsingV2API_FlushesHeartbeatInTheFirstFlush {
  154. // Given
  155. FIRHeartbeatLogger *heartbeatLogger = self.heartbeatLogger;
  156. [heartbeatLogger log];
  157. // When
  158. FIRDailyHeartbeatCode heartbeatInfoCode = [heartbeatLogger heartbeatCodeForToday];
  159. FIRHeartbeatsPayload *heartbeatsPayload = [heartbeatLogger flushHeartbeatsIntoPayload];
  160. // Then
  161. XCTAssertEqual(heartbeatInfoCode, FIRDailyHeartbeatCodeSome);
  162. [self assertHeartbeatsPayloadIsEmpty:heartbeatsPayload];
  163. }
  164. - (void)testLogAndFlushUsingV2API_AndThenFlushAgainUsingV1API_FlushesHeartbeatInTheFirstFlush {
  165. // Given
  166. FIRHeartbeatLogger *heartbeatLogger = self.heartbeatLogger;
  167. NSString *expectedDate = [[self class] formattedStringForDate:[NSDate date]];
  168. [heartbeatLogger log];
  169. // When
  170. FIRHeartbeatsPayload *heartbeatsPayload = [heartbeatLogger flushHeartbeatsIntoPayload];
  171. FIRDailyHeartbeatCode heartbeatInfoCode = [heartbeatLogger heartbeatCodeForToday];
  172. // Then
  173. [self assertEncodedPayloadHeader:FIRHeaderValueFromHeartbeatsPayload(heartbeatsPayload)
  174. isEqualToPayloadJSON:@{
  175. @"version" : @2,
  176. @"heartbeats" : @[ @{@"agent" : @"dummy_agent", @"dates" : @[ expectedDate ]} ]
  177. }];
  178. XCTAssertEqual(heartbeatInfoCode, FIRDailyHeartbeatCodeNone);
  179. }
  180. - (void)testHeartbeatLoggersWithSameIDShareTheSameStorage {
  181. // Given
  182. FIRHeartbeatLogger *heartbeatLogger1 =
  183. [[FIRHeartbeatLogger alloc] initWithAppID:[[self class] dummyAppID]
  184. userAgentProvider:[[self class] dummyUserAgentProvider]];
  185. FIRHeartbeatLogger *heartbeatLogger2 =
  186. [[FIRHeartbeatLogger alloc] initWithAppID:[[self class] dummyAppID]
  187. userAgentProvider:[[self class] dummyUserAgentProvider]];
  188. NSString *expectedDate = [[self class] formattedStringForDate:[NSDate date]];
  189. // When
  190. [heartbeatLogger1 log];
  191. // Then
  192. FIRHeartbeatsPayload *heartbeatsPayload = [heartbeatLogger2 flushHeartbeatsIntoPayload];
  193. [self assertEncodedPayloadHeader:FIRHeaderValueFromHeartbeatsPayload(heartbeatsPayload)
  194. isEqualToPayloadJSON:@{
  195. @"version" : @2,
  196. @"heartbeats" : @[ @{@"agent" : @"dummy_agent", @"dates" : @[ expectedDate ]} ]
  197. }];
  198. [self assertHeartbeatLoggerFlushesEmptyPayload:heartbeatLogger1];
  199. }
  200. - (void)testLoggingAHeartbeatDoesNotDependOnUserAgent {
  201. // Given
  202. __block NSString *dummyUserAgent = @"dummy_agent_1";
  203. __auto_type dummyUserAgentProvider = ^NSString * {
  204. return dummyUserAgent;
  205. };
  206. FIRHeartbeatLogger *heartbeatLogger =
  207. [[FIRHeartbeatLogger alloc] initWithAppID:@"testLoggingAHeartbeatDoesNotDependOnUserAgent"
  208. userAgentProvider:dummyUserAgentProvider];
  209. NSString *expectedDate = [[self class] formattedStringForDate:[NSDate date]];
  210. [heartbeatLogger log];
  211. FIRHeartbeatsPayload *heartbeatsPayload = [heartbeatLogger flushHeartbeatsIntoPayload];
  212. // When
  213. dummyUserAgent = @"dummy_agent_2";
  214. [heartbeatLogger log];
  215. // Then
  216. [self assertEncodedPayloadHeader:FIRHeaderValueFromHeartbeatsPayload(heartbeatsPayload)
  217. isEqualToPayloadJSON:@{
  218. @"version" : @2,
  219. @"heartbeats" : @[ @{@"agent" : @"dummy_agent_1", @"dates" : @[ expectedDate ]} ]
  220. }];
  221. [self assertHeartbeatLoggerFlushesEmptyPayload:heartbeatLogger];
  222. }
  223. #pragma mark - Assertions
  224. - (void)assertEncodedPayloadHeader:(NSString *)payloadHeader
  225. isEqualToPayloadJSON:(NSDictionary *)payloadJSON {
  226. NSData *payloadJSONData = [NSJSONSerialization dataWithJSONObject:payloadJSON
  227. options:NSJSONWritingPrettyPrinted
  228. error:nil];
  229. NSString *payloadJSONString = [[NSString alloc] initWithData:payloadJSONData
  230. encoding:NSUTF8StringEncoding];
  231. [FIRHeartbeatLoggingTestUtils assertEncodedPayloadString:payloadHeader
  232. isEqualToLiteralString:payloadJSONString
  233. withError:nil];
  234. }
  235. - (void)assertHeartbeatsPayloadIsEmpty:(FIRHeartbeatsPayload *)heartbeatsPayload {
  236. XCTAssertNil(FIRHeaderValueFromHeartbeatsPayload(heartbeatsPayload));
  237. }
  238. - (void)assertHeartbeatLoggerFlushesEmptyPayload:(FIRHeartbeatLogger *)heartbeatLogger {
  239. FIRHeartbeatsPayload *heartbeatsPayload = [heartbeatLogger flushHeartbeatsIntoPayload];
  240. [self assertHeartbeatsPayloadIsEmpty:heartbeatsPayload];
  241. }
  242. @end