FIRMessagingAnalyticsTest.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. /*
  2. * Copyright 2018 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <OCMock/OCMock.h>
  17. #import <XCTest/XCTest.h>
  18. #import "Interop/Analytics/Public/FIRInteropEventNames.h"
  19. #import "Interop/Analytics/Public/FIRInteropParameterNames.h"
  20. #import "FirebaseMessaging/Sources/FIRMessagingAnalytics.h"
  21. // Analytics tracking is iOS only feature.
  22. #if TARGET_OS_IOS
  23. static NSString *const kFIRParameterLabel = @"label";
  24. static NSString *const kReengagementSource = @"Firebase";
  25. static NSString *const kReengagementMedium = @"notification";
  26. static NSString *const kFIREventOriginFCM = @"fcm";
  27. static const NSTimeInterval kAsyncTestTimeout = 0.5;
  28. typedef void (^FakeAnalyticsLogEventHandler)(NSString *origin,
  29. NSString *name,
  30. NSDictionary *parameters);
  31. typedef void (^FakeAnalyticsUserPropertyHandler)(NSString *origin, NSString *name, id value);
  32. @interface FakeAnalytics : NSObject <FIRAnalyticsInterop>
  33. - (instancetype)initWithEventHandler:(FakeAnalyticsLogEventHandler)eventHandler;
  34. - (instancetype)initWithUserPropertyHandler:(FakeAnalyticsUserPropertyHandler)userPropertyHandler;
  35. @end
  36. @implementation FakeAnalytics
  37. static FakeAnalyticsLogEventHandler _eventHandler;
  38. static FakeAnalyticsLogEventHandler _userPropertyHandler;
  39. - (instancetype)initWithEventHandler:(FakeAnalyticsLogEventHandler)eventHandler {
  40. self = [super init];
  41. if (self) {
  42. _eventHandler = eventHandler;
  43. }
  44. return self;
  45. }
  46. - (instancetype)initWithUserPropertyHandler:(FakeAnalyticsUserPropertyHandler)userPropertyHandler {
  47. self = [super init];
  48. if (self) {
  49. _userPropertyHandler = userPropertyHandler;
  50. }
  51. return self;
  52. }
  53. - (void)logEventWithOrigin:(nonnull NSString *)origin
  54. name:(nonnull NSString *)name
  55. parameters:(nullable NSDictionary<NSString *, id> *)parameters {
  56. if (_eventHandler) {
  57. _eventHandler(origin, name, parameters);
  58. }
  59. }
  60. - (void)setUserPropertyWithOrigin:(nonnull NSString *)origin
  61. name:(nonnull NSString *)name
  62. value:(nonnull id)value {
  63. if (_userPropertyHandler) {
  64. _userPropertyHandler(origin, name, value);
  65. }
  66. }
  67. // Stubs
  68. - (void)clearConditionalUserProperty:(nonnull NSString *)userPropertyName
  69. clearEventName:(nonnull NSString *)clearEventName
  70. clearEventParameters:(nonnull NSDictionary *)clearEventParameters {
  71. }
  72. - (NSInteger)maxUserProperties:(nonnull NSString *)origin {
  73. return -1;
  74. }
  75. - (void)checkLastNotificationForOrigin:(nonnull NSString *)origin
  76. queue:(nonnull dispatch_queue_t)queue
  77. callback:(nonnull void (^)(NSString *_Nullable))
  78. currentLastNotificationProperty {
  79. }
  80. - (void)registerAnalyticsListener:(nonnull id<FIRAnalyticsInteropListener>)listener
  81. withOrigin:(nonnull NSString *)origin {
  82. }
  83. - (void)unregisterAnalyticsListenerWithOrigin:(nonnull NSString *)origin {
  84. }
  85. - (void)clearConditionalUserProperty:(nonnull NSString *)userPropertyName
  86. forOrigin:(nonnull NSString *)origin
  87. clearEventName:(nonnull NSString *)clearEventName
  88. clearEventParameters:
  89. (nonnull NSDictionary<NSString *, NSString *> *)clearEventParameters {
  90. }
  91. - (nonnull NSArray<NSDictionary<NSString *, NSString *> *> *)
  92. conditionalUserProperties:(nonnull NSString *)origin
  93. propertyNamePrefix:(nonnull NSString *)propertyNamePrefix {
  94. return nil;
  95. }
  96. - (void)setConditionalUserProperty:(nonnull NSDictionary<NSString *, id> *)conditionalUserProperty {
  97. }
  98. - (void)getUserPropertiesWithCallback:(nonnull FIRAInteropUserPropertiesCallback)callback {
  99. }
  100. @end
  101. @interface FIRMessagingAnalytics (ExposedForTest)
  102. + (BOOL)canLogNotification:(NSDictionary *)notification;
  103. + (NSMutableDictionary *)paramsForEvent:(NSString *)event
  104. withNotification:(NSDictionary *)notification;
  105. + (void)logAnalyticsEventWithOrigin:(NSString *)origin
  106. name:(NSString *)name
  107. parameters:(NSDictionary *)params
  108. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
  109. + (void)logUserPropertyForConversionTracking:(NSDictionary *)notification
  110. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
  111. + (void)logOpenNotification:(NSDictionary *)notification
  112. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
  113. + (void)logForegroundNotification:(NSDictionary *)notification
  114. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
  115. + (void)logEvent:(NSString *)event
  116. withNotification:(NSDictionary *)notification
  117. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
  118. ;
  119. + (BOOL)isDisplayNotification:(NSDictionary *)notification;
  120. @end
  121. @interface FIRMessagingAnalyticsTest : XCTestCase
  122. @property(nonatomic, readwrite, strong) id logClassMock;
  123. @end
  124. @implementation FIRMessagingAnalyticsTest
  125. - (void)setUp {
  126. [super setUp];
  127. self.logClassMock = OCMClassMock([FIRMessagingAnalytics class]);
  128. }
  129. - (void)tearDown {
  130. _eventHandler = nil;
  131. _userPropertyHandler = nil;
  132. [self.logClassMock stopMocking];
  133. [super tearDown];
  134. }
  135. - (void)testCanLogNotification {
  136. NSDictionary *notification = @{};
  137. XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
  138. notification = @{@"google.c.a.e" : @1};
  139. XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
  140. notification = @{@"aps" : @{@"alert" : @"to check the reporting format"}};
  141. XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
  142. notification = @{@"aps" : @{@"alert" : @"to check the reporting format"}, @"google.c.a.e" : @"0"};
  143. XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
  144. notification = @{
  145. @"aps" : @{@"alert" : @"to check the reporting format"},
  146. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  147. @"gcm.n.e" : @"1",
  148. @"google.c.a.c_id" : @"575315420755741863",
  149. @"google.c.a.e" : @"1",
  150. @"google.c.a.ts" : @"1522880044",
  151. @"google.c.a.udt" : @"0"
  152. };
  153. XCTAssertTrue([FIRMessagingAnalytics canLogNotification:notification]);
  154. notification = @{
  155. @"aps" : @{@"content-available" : @"1"},
  156. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  157. @"google.c.a.e" : @"1",
  158. @"google.c.a.ts" : @"1522880044",
  159. @"google.c.a.udt" : @"0"
  160. };
  161. XCTAssertTrue([FIRMessagingAnalytics canLogNotification:notification]);
  162. }
  163. - (void)testEmptyNotification {
  164. NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:@"" withNotification:@{}];
  165. XCTAssertNil(params);
  166. }
  167. - (void)testNoParamsIfAnalyticsIsNotEnabled {
  168. NSDictionary *notification = @{
  169. @"aps" : @{@"alert" : @"to check the reporting format"},
  170. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  171. @"gcm.n.e" : @"1",
  172. @"google.c.a.c_id" : @"575315420755741863",
  173. @"google.c.a.ts" : @"1522880044",
  174. @"google.c.a.udt" : @"0"
  175. };
  176. NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:@""
  177. withNotification:notification];
  178. XCTAssertNil(params);
  179. }
  180. - (void)testNoParamsIfEmpty {
  181. NSDictionary *notification = @{
  182. @"google.c.a.e" : @"1",
  183. };
  184. NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:@""
  185. withNotification:notification];
  186. XCTAssertNotNil(params);
  187. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  188. FakeAnalytics *analytics = [[FakeAnalytics alloc]
  189. initWithEventHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
  190. XCTAssertEqualObjects(origin, kFIREventOriginFCM);
  191. XCTAssertEqualObjects(name, @"_cmp");
  192. XCTAssertEqual([parameters count], 0);
  193. [expectation fulfill];
  194. }];
  195. [FIRMessagingAnalytics logEvent:kFIRIEventFirebaseCampaign
  196. withNotification:notification
  197. toAnalytics:analytics];
  198. [self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
  199. }
  200. - (void)testParamForEventAndNotification {
  201. NSDictionary *notification = @{
  202. @"aps" : @{@"alert" : @"to check the reporting format"},
  203. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  204. @"gcm.n.e" : @"1",
  205. @"google.c.a.c_l" : @"Hello World",
  206. @"google.c.a.c_id" : @"575315420755741863",
  207. @"google.c.a.e" : @"1",
  208. @"google.c.a.ts" : @"1522880044",
  209. @"google.c.a.udt" : @"0",
  210. @"google.c.a.m_l" : @"developer's customized label",
  211. @"from" : @"/topics/news",
  212. };
  213. NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen
  214. withNotification:notification];
  215. XCTAssertNotNil(params);
  216. XCTAssertEqualObjects(params[kFIRIParameterMessageIdentifier], @"575315420755741863");
  217. XCTAssertEqualObjects(params[kFIRIParameterMessageName], @"Hello World");
  218. XCTAssertEqualObjects(params[kFIRParameterLabel], @"developer's customized label");
  219. XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
  220. XCTAssertEqualObjects([params[kFIRIParameterMessageTime] stringValue], @"1522880044");
  221. XCTAssertEqualObjects(params[kFIRIParameterMessageDeviceTime], @"0");
  222. }
  223. - (void)testInvalidDataInParamsForLogging {
  224. NSString *composerIdentifier = @"Hellow World";
  225. NSDictionary *notification = @{
  226. @"google.c.a.e" : @(YES),
  227. @"google.c.a.c_l" : composerIdentifier,
  228. @"google.c.a.c_id" : @"575315420755741863",
  229. @"google.c.a.m_l" : @"developer's customized label",
  230. @"google.c.a.ts" : @"1522880044",
  231. @"from" : @"/topics/news",
  232. @"google.c.a.udt" : @"0",
  233. };
  234. NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen
  235. withNotification:notification];
  236. XCTAssertNil(params);
  237. notification = @{
  238. @"google.c.a.e" : @"1",
  239. @"google.c.a.c_l" : [composerIdentifier dataUsingEncoding:NSUTF8StringEncoding],
  240. @"google.c.a.c_id" : @"575315420755741863",
  241. @"google.c.a.m_l" : @"developer's customized label",
  242. @"google.c.a.ts" : @"1522880044",
  243. @"from" : @"/topics/news",
  244. @"google.c.a.udt" : @"0",
  245. };
  246. params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen
  247. withNotification:notification];
  248. XCTAssertNil(params[kFIRIParameterMessageName]);
  249. XCTAssertEqualObjects(params[kFIRIParameterMessageIdentifier], @"575315420755741863");
  250. XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
  251. notification = @{
  252. @"google.c.a.e" : @"1",
  253. @"google.c.a.c_l" : composerIdentifier,
  254. @"google.c.a.c_id" : @(575315420755741863),
  255. @"google.c.a.m_l" : @"developer's customized label",
  256. @"google.c.a.ts" : @"1522880044",
  257. @"from" : @"/topics/news",
  258. @"google.c.a.udt" : @"0",
  259. };
  260. params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen
  261. withNotification:notification];
  262. XCTAssertEqualObjects(params[kFIRIParameterMessageName], composerIdentifier);
  263. XCTAssertNil(params[kFIRIParameterMessageIdentifier]);
  264. XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
  265. notification = @{
  266. @"google.c.a.e" : @"1",
  267. @"google.c.a.c_l" : composerIdentifier,
  268. @"google.c.a.c_id" : @"575315420755741863",
  269. @"google.c.a.m_l" : @"developer's customized label",
  270. @"google.c.a.ts" : @"0",
  271. @"from" : @"/topics/news",
  272. @"google.c.a.udt" : @"12345678",
  273. };
  274. params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen
  275. withNotification:notification];
  276. XCTAssertEqualObjects(params[kFIRIParameterMessageName], composerIdentifier);
  277. XCTAssertEqualObjects(params[kFIRIParameterMessageIdentifier], @"575315420755741863");
  278. XCTAssertEqualObjects(params[kFIRParameterLabel], @"developer's customized label");
  279. XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
  280. XCTAssertNil(params[kFIRIParameterMessageTime]);
  281. XCTAssertEqualObjects(params[kFIRIParameterMessageDeviceTime], @"12345678");
  282. notification = @{
  283. @"google.c.a.e" : @"1",
  284. @"google.c.a.c_l" : composerIdentifier,
  285. @"google.c.a.c_id" : @"575315420755741863",
  286. @"google.c.a.m_l" : @"developer's customized label",
  287. @"google.c.a.ts" : @(0),
  288. @"from" : @"/topics/news",
  289. @"google.c.a.udt" : @"12345678",
  290. };
  291. params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen
  292. withNotification:notification];
  293. XCTAssertEqualObjects(params[kFIRIParameterMessageName], composerIdentifier);
  294. XCTAssertNil(params[kFIRIParameterMessageTime]);
  295. XCTAssertEqualObjects(params[kFIRIParameterMessageDeviceTime], @"12345678");
  296. }
  297. - (void)testConversionTracking {
  298. // Notification contains "google.c.a.tc" key.
  299. NSDictionary *notification = @{
  300. @"aps" : @{@"alert" : @"to check the reporting format"},
  301. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  302. @"gcm.n.e" : @"1",
  303. @"google.c.a.c_l" : @"Hello World",
  304. @"google.c.a.c_id" : @"575315420755741863",
  305. @"google.c.a.e" : @"1",
  306. @"google.c.a.ts" : @"1522880044",
  307. @"google.c.a.udt" : @"0",
  308. @"google.c.a.m_l" : @"developer's customized label",
  309. @"google.c.a.tc" : @"1",
  310. @"from" : @"/topics/news",
  311. };
  312. NSDictionary *params = @{
  313. kFIRIParameterSource : kReengagementSource,
  314. kFIRIParameterMedium : kReengagementMedium,
  315. kFIRIParameterCampaign : @"575315420755741863"
  316. };
  317. __block XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  318. FakeAnalytics *analytics = [[FakeAnalytics alloc]
  319. initWithEventHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
  320. XCTAssertEqualObjects(origin, kFIREventOriginFCM);
  321. XCTAssertEqualObjects(name, @"_cmp");
  322. XCTAssertEqualObjects(parameters, params);
  323. [expectation fulfill];
  324. expectation = nil;
  325. }];
  326. [FIRMessagingAnalytics logUserPropertyForConversionTracking:notification toAnalytics:analytics];
  327. [self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
  328. }
  329. - (void)testConversionTrackingUserProperty {
  330. // Notification contains "google.c.a.tc" key.
  331. NSDictionary *notification = @{
  332. @"aps" : @{@"alert" : @"to check the reporting format"},
  333. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  334. @"gcm.n.e" : @"1",
  335. @"google.c.a.c_l" : @"Hello World",
  336. @"google.c.a.c_id" : @"575315420755741863",
  337. @"google.c.a.e" : @"1",
  338. @"google.c.a.ts" : @"1522880044",
  339. @"google.c.a.udt" : @"0",
  340. @"google.c.a.m_l" : @"developer's customized label",
  341. @"google.c.a.tc" : @"1",
  342. @"from" : @"/topics/news",
  343. };
  344. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  345. FakeAnalytics *analytics = [[FakeAnalytics alloc]
  346. initWithUserPropertyHandler:^(NSString *origin, NSString *name, id value) {
  347. XCTAssertEqualObjects(origin, kFIREventOriginFCM);
  348. XCTAssertEqualObjects(name, @"_ln");
  349. XCTAssertEqualObjects(value, @"575315420755741863");
  350. [expectation fulfill];
  351. }];
  352. [FIRMessagingAnalytics logUserPropertyForConversionTracking:notification toAnalytics:analytics];
  353. [self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
  354. }
  355. - (void)testNoConversionTracking {
  356. // Notification contains "google.c.a.tc" key.
  357. NSDictionary *notification = @{
  358. @"aps" : @{@"alert" : @"to check the reporting format"},
  359. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  360. @"gcm.n.e" : @"1",
  361. @"google.c.a.c_l" : @"Hello World",
  362. @"google.c.a.c_id" : @"575315420755741863",
  363. @"google.c.a.e" : @"1",
  364. @"google.c.a.ts" : @"1522880044",
  365. @"google.c.a.udt" : @"0",
  366. @"google.c.a.m_l" : @"developer's customized label",
  367. @"from" : @"/topics/news",
  368. };
  369. FakeAnalytics *analytics = [[FakeAnalytics alloc]
  370. initWithEventHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
  371. XCTAssertTrue(NO);
  372. }];
  373. [FIRMessagingAnalytics logUserPropertyForConversionTracking:notification toAnalytics:analytics];
  374. }
  375. #if !SWIFT_PACKAGE
  376. // This test depends on a sharedApplication which is not available in the Swift PM test env.
  377. - (void)testLogMessage {
  378. NSDictionary *notification = @{
  379. @"google.c.a.e" : @"1",
  380. @"aps" : @{@"alert" : @"to check the reporting format"},
  381. };
  382. [FIRMessagingAnalytics logMessage:notification toAnalytics:nil];
  383. OCMVerify([self.logClassMock logEvent:OCMOCK_ANY withNotification:notification toAnalytics:nil]);
  384. }
  385. #endif
  386. - (void)testLogOpenNotification {
  387. NSDictionary *notification = @{
  388. @"google.c.a.e" : @"1",
  389. @"aps" : @{@"alert" : @"to check the reporting format"},
  390. };
  391. [FIRMessagingAnalytics logOpenNotification:notification toAnalytics:nil];
  392. OCMVerify([self.logClassMock logUserPropertyForConversionTracking:notification toAnalytics:nil]);
  393. OCMVerify([self.logClassMock logEvent:kFIRIEventNotificationOpen
  394. withNotification:notification
  395. toAnalytics:nil]);
  396. }
  397. - (void)testDisplayNotification {
  398. NSDictionary *notification = @{
  399. @"google.c.a.e" : @"1",
  400. };
  401. XCTAssertFalse([FIRMessagingAnalytics isDisplayNotification:notification]);
  402. notification = @{
  403. @"aps" : @{@"alert" : @"to check the reporting format"},
  404. };
  405. XCTAssertTrue([FIRMessagingAnalytics isDisplayNotification:notification]);
  406. notification = @{
  407. @"google.c.a.e" : @"1",
  408. @"aps" : @{@"alert" : @{@"title" : @"Hello World"}},
  409. };
  410. XCTAssertTrue([FIRMessagingAnalytics isDisplayNotification:notification]);
  411. notification = @{
  412. @"google.c.a.e" : @"1",
  413. @"aps" : @{@"alert" : @{@"body" : @"This is the body of notification."}},
  414. };
  415. XCTAssertTrue([FIRMessagingAnalytics isDisplayNotification:notification]);
  416. notification = @{
  417. @"google.c.a.e" : @"1",
  418. @"aps" :
  419. @{@"alert" : @{@"title" : @"Hello World", @"body" : @"This is the body of notification."}},
  420. };
  421. XCTAssertTrue([FIRMessagingAnalytics isDisplayNotification:notification]);
  422. notification = @{
  423. @"google.c.a.e" : @"1",
  424. @"aps" : @{@"alert" : @{@"subtitle" : @"Hello World"}},
  425. };
  426. XCTAssertTrue([FIRMessagingAnalytics isDisplayNotification:notification]);
  427. }
  428. @end
  429. #endif // TARGET_OS_IOS