FIRMessagingAnalyticsTest.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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 <FirebaseAnalyticsInterop/FIRInteropEventNames.h>
  19. #import <FirebaseAnalyticsInterop/FIRInteropParameterNames.h>
  20. #import "Firebase/Messaging/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 kAsyncTestTimout = 0.5;
  28. typedef void (^FakeAnalyticsLogEventHandler)(NSString *origin,
  29. NSString *name,
  30. NSDictionary *parameters);
  31. typedef void (^FakeAnalyticsUserPropertyHandler)(NSString *origin,
  32. NSString *name,
  33. id value);
  34. @interface FakeAnalytics : NSObject <FIRAnalyticsInterop>
  35. - (instancetype)initWithEventHandler:(FakeAnalyticsLogEventHandler)eventHandler;
  36. - (instancetype)initWithUserPropertyHandler:(FakeAnalyticsUserPropertyHandler)userPropertyHandler;
  37. @end
  38. @implementation FakeAnalytics
  39. static FakeAnalyticsLogEventHandler _eventHandler;
  40. static FakeAnalyticsLogEventHandler _userPropertyHandler;
  41. - (instancetype)initWithEventHandler:(FakeAnalyticsLogEventHandler)eventHandler {
  42. self = [super init];
  43. if (self) {
  44. _eventHandler = eventHandler;
  45. }
  46. return self;
  47. }
  48. - (instancetype)initWithUserPropertyHandler:(FakeAnalyticsUserPropertyHandler)userPropertyHandler {
  49. self = [super init];
  50. if (self) {
  51. _userPropertyHandler = userPropertyHandler;
  52. }
  53. return self;
  54. }
  55. - (void)logEventWithOrigin:(nonnull NSString *)origin
  56. name:(nonnull NSString *)name
  57. parameters:(nullable NSDictionary<NSString *, id> *)parameters {
  58. if (_eventHandler) {
  59. _eventHandler(origin, name, parameters);
  60. }
  61. }
  62. - (void)setUserPropertyWithOrigin:(nonnull NSString *)origin
  63. name:(nonnull NSString *)name
  64. value:(nonnull id)value {
  65. if (_userPropertyHandler) {
  66. _userPropertyHandler(origin, name, value);
  67. }
  68. }
  69. // Stubs
  70. - (void)clearConditionalUserProperty:(nonnull NSString *)userPropertyName
  71. clearEventName:(nonnull NSString *)clearEventName
  72. clearEventParameters:(nonnull NSDictionary *)clearEventParameters {
  73. }
  74. - (NSInteger)maxUserProperties:(nonnull NSString *)origin {
  75. return -1;
  76. }
  77. - (void)checkLastNotificationForOrigin:(nonnull NSString *)origin
  78. queue:(nonnull dispatch_queue_t)queue
  79. callback:(nonnull void (^)(NSString *_Nullable))
  80. currentLastNotificationProperty {
  81. }
  82. - (void)registerAnalyticsListener:(nonnull id<FIRAnalyticsInteropListener>)listener
  83. withOrigin:(nonnull NSString *)origin {
  84. }
  85. - (void)unregisterAnalyticsListenerWithOrigin:(nonnull NSString *)origin {
  86. }
  87. - (void)clearConditionalUserProperty:(nonnull NSString *)userPropertyName forOrigin:(nonnull NSString *)origin clearEventName:(nonnull NSString *)clearEventName clearEventParameters:(nonnull NSDictionary<NSString *,NSString *> *)clearEventParameters {
  88. }
  89. - (nonnull NSArray<NSDictionary<NSString *,NSString *> *> *)conditionalUserProperties:(nonnull NSString *)origin propertyNamePrefix:(nonnull NSString *)propertyNamePrefix {
  90. return nil;
  91. }
  92. - (void)setConditionalUserProperty:(nonnull NSDictionary<NSString *,id> *)conditionalUserProperty {
  93. }
  94. - (void)getUserPropertiesWithCallback:(nonnull FIRAInteropUserPropertiesCallback)callback {
  95. }
  96. @end
  97. @interface FIRMessagingAnalytics (ExposedForTest)
  98. + (BOOL)canLogNotification:(NSDictionary *)notification;
  99. + (NSMutableDictionary *)paramsForEvent:(NSString *)event
  100. withNotification:(NSDictionary *)notification;
  101. + (void)logAnalyticsEventWithOrigin:(NSString *)origin
  102. name:(NSString *)name
  103. parameters:(NSDictionary *)params
  104. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
  105. + (void)logUserPropertyForConversionTracking:(NSDictionary *)notification
  106. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
  107. + (void)logOpenNotification:(NSDictionary *)notification
  108. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
  109. + (void)logForegroundNotification:(NSDictionary *)notification
  110. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics;
  111. + (void)logEvent:(NSString *)event
  112. withNotification:(NSDictionary *)notification
  113. toAnalytics:(id<FIRAnalyticsInterop> _Nullable)analytics ;;
  114. @end
  115. @interface FIRMessagingAnalyticsTest : XCTestCase
  116. @property(nonatomic, readwrite, strong) id logClassMock;
  117. @end
  118. @implementation FIRMessagingAnalyticsTest
  119. - (void)setUp {
  120. [super setUp];
  121. self.logClassMock = OCMClassMock([FIRMessagingAnalytics class]);
  122. }
  123. - (void)tearDown {
  124. _eventHandler = nil;
  125. _userPropertyHandler = nil;
  126. [self.logClassMock stopMocking];
  127. [super tearDown];
  128. }
  129. - (void)testCanLogNotification {
  130. NSDictionary *notification = @{};
  131. XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
  132. notification = @{@"google.c.a.e" : @1};
  133. XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
  134. notification = @{@"aps" : @{@"alert" : @"to check the reporting format"}};
  135. XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
  136. notification = @{@"aps" : @{@"alert" : @"to check the reporting format"}, @"google.c.a.e" : @"0"};
  137. XCTAssertFalse([FIRMessagingAnalytics canLogNotification:notification]);
  138. notification = @{
  139. @"aps" : @{@"alert" : @"to check the reporting format"},
  140. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  141. @"gcm.n.e" : @"1",
  142. @"google.c.a.c_id" : @"575315420755741863",
  143. @"google.c.a.e" : @"1",
  144. @"google.c.a.ts" : @"1522880044",
  145. @"google.c.a.udt" : @"0"
  146. };
  147. XCTAssertTrue([FIRMessagingAnalytics canLogNotification:notification]);
  148. notification = @{
  149. @"aps" : @{@"content-available" : @"1"},
  150. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  151. @"google.c.a.e" : @"1",
  152. @"google.c.a.ts" : @"1522880044",
  153. @"google.c.a.udt" : @"0"
  154. };
  155. XCTAssertTrue([FIRMessagingAnalytics canLogNotification:notification]);
  156. }
  157. - (void)testEmptyNotification {
  158. NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:@"" withNotification:@{}];
  159. XCTAssertNil(params);
  160. }
  161. - (void)testNoParamsIfAnalyticsIsNotEnabled {
  162. NSDictionary *notification = @{
  163. @"aps" : @{@"alert" : @"to check the reporting format"},
  164. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  165. @"gcm.n.e" : @"1",
  166. @"google.c.a.c_id" : @"575315420755741863",
  167. @"google.c.a.ts" : @"1522880044",
  168. @"google.c.a.udt" : @"0"
  169. };
  170. NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:@"" withNotification:notification];
  171. XCTAssertNil(params);
  172. }
  173. - (void)testNoParamsIfEmpty {
  174. NSDictionary *notification = @{
  175. @"google.c.a.e" : @"1",
  176. };
  177. NSMutableDictionary *params = [FIRMessagingAnalytics paramsForEvent:@""
  178. withNotification:notification];
  179. XCTAssertNotNil(params);
  180. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  181. FakeAnalytics *analytics = [[FakeAnalytics alloc]
  182. initWithEventHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
  183. XCTAssertEqualObjects(origin, kFIREventOriginFCM);
  184. XCTAssertEqualObjects(name, @"_cmp");
  185. XCTAssertEqual([parameters count], 0);
  186. [expectation fulfill];
  187. }];
  188. [FIRMessagingAnalytics logEvent:kFIRIEventFirebaseCampaign
  189. withNotification:notification
  190. toAnalytics:analytics];
  191. [self waitForExpectationsWithTimeout:kAsyncTestTimout handler:nil];
  192. }
  193. - (void)testParamForEventAndNotification {
  194. NSDictionary *notification = @{
  195. @"aps" : @{@"alert" : @"to check the reporting format"},
  196. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  197. @"gcm.n.e" : @"1",
  198. @"google.c.a.c_l" : @"Hello World",
  199. @"google.c.a.c_id" : @"575315420755741863",
  200. @"google.c.a.e" : @"1",
  201. @"google.c.a.ts" : @"1522880044",
  202. @"google.c.a.udt" : @"0",
  203. @"google.c.a.m_l" : @"developer's customized label",
  204. @"from" : @"/topics/news",
  205. };
  206. NSMutableDictionary *params =
  207. [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
  208. XCTAssertNotNil(params);
  209. XCTAssertEqualObjects(params[kFIRIParameterMessageIdentifier], @"575315420755741863");
  210. XCTAssertEqualObjects(params[kFIRIParameterMessageName], @"Hello World");
  211. XCTAssertEqualObjects(params[kFIRParameterLabel], @"developer's customized label");
  212. XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
  213. XCTAssertEqualObjects([params[kFIRIParameterMessageTime] stringValue], @"1522880044");
  214. XCTAssertEqualObjects(params[kFIRIParameterMessageDeviceTime], @"0");
  215. }
  216. - (void)testInvalidDataInParamsForLogging {
  217. NSString *composerIdentifier = @"Hellow World";
  218. NSDictionary *notification = @{
  219. @"google.c.a.e" : @(YES),
  220. @"google.c.a.c_l" : composerIdentifier,
  221. @"google.c.a.c_id" : @"575315420755741863",
  222. @"google.c.a.m_l" : @"developer's customized label",
  223. @"google.c.a.ts" : @"1522880044",
  224. @"from" : @"/topics/news",
  225. @"google.c.a.udt" : @"0",
  226. };
  227. NSMutableDictionary *params =
  228. [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
  229. XCTAssertNil(params);
  230. notification = @{
  231. @"google.c.a.e" : @"1",
  232. @"google.c.a.c_l" : [composerIdentifier dataUsingEncoding:NSUTF8StringEncoding],
  233. @"google.c.a.c_id" : @"575315420755741863",
  234. @"google.c.a.m_l" : @"developer's customized label",
  235. @"google.c.a.ts" : @"1522880044",
  236. @"from" : @"/topics/news",
  237. @"google.c.a.udt" : @"0",
  238. };
  239. params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
  240. XCTAssertNil(params[kFIRIParameterMessageName]);
  241. XCTAssertEqualObjects(params[kFIRIParameterMessageIdentifier], @"575315420755741863");
  242. XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
  243. notification = @{
  244. @"google.c.a.e" : @"1",
  245. @"google.c.a.c_l" : composerIdentifier,
  246. @"google.c.a.c_id" : @(575315420755741863),
  247. @"google.c.a.m_l" : @"developer's customized label",
  248. @"google.c.a.ts" : @"1522880044",
  249. @"from" : @"/topics/news",
  250. @"google.c.a.udt" : @"0",
  251. };
  252. params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
  253. XCTAssertEqualObjects(params[kFIRIParameterMessageName], composerIdentifier);
  254. XCTAssertNil(params[kFIRIParameterMessageIdentifier]);
  255. XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
  256. notification = @{
  257. @"google.c.a.e" : @"1",
  258. @"google.c.a.c_l" : composerIdentifier,
  259. @"google.c.a.c_id" : @"575315420755741863",
  260. @"google.c.a.m_l" : @"developer's customized label",
  261. @"google.c.a.ts" : @"0",
  262. @"from" : @"/topics/news",
  263. @"google.c.a.udt" : @"12345678",
  264. };
  265. params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
  266. XCTAssertEqualObjects(params[kFIRIParameterMessageName], composerIdentifier);
  267. XCTAssertEqualObjects(params[kFIRIParameterMessageIdentifier], @"575315420755741863");
  268. XCTAssertEqualObjects(params[kFIRParameterLabel], @"developer's customized label");
  269. XCTAssertEqualObjects(params[kFIRIParameterTopic], @"/topics/news");
  270. XCTAssertNil(params[kFIRIParameterMessageTime]);
  271. XCTAssertEqualObjects(params[kFIRIParameterMessageDeviceTime], @"12345678");
  272. notification = @{
  273. @"google.c.a.e" : @"1",
  274. @"google.c.a.c_l" : composerIdentifier,
  275. @"google.c.a.c_id" : @"575315420755741863",
  276. @"google.c.a.m_l" : @"developer's customized label",
  277. @"google.c.a.ts" : @(0),
  278. @"from" : @"/topics/news",
  279. @"google.c.a.udt" : @"12345678",
  280. };
  281. params = [FIRMessagingAnalytics paramsForEvent:kFIRIEventNotificationOpen withNotification:notification];
  282. XCTAssertEqualObjects(params[kFIRIParameterMessageName], composerIdentifier);
  283. XCTAssertNil(params[kFIRIParameterMessageTime]);
  284. XCTAssertEqualObjects(params[kFIRIParameterMessageDeviceTime], @"12345678");
  285. }
  286. - (void)testConversionTracking {
  287. // Notification contains "google.c.a.tc" key.
  288. NSDictionary *notification = @{
  289. @"aps" : @{@"alert" : @"to check the reporting format"},
  290. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  291. @"gcm.n.e" : @"1",
  292. @"google.c.a.c_l" : @"Hello World",
  293. @"google.c.a.c_id" : @"575315420755741863",
  294. @"google.c.a.e" : @"1",
  295. @"google.c.a.ts" : @"1522880044",
  296. @"google.c.a.udt" : @"0",
  297. @"google.c.a.m_l" : @"developer's customized label",
  298. @"google.c.a.tc" : @"1",
  299. @"from" : @"/topics/news",
  300. };
  301. NSDictionary *params = @{
  302. kFIRIParameterSource : kReengagementSource,
  303. kFIRIParameterMedium : kReengagementMedium,
  304. kFIRIParameterCampaign : @"575315420755741863"
  305. };
  306. __block XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  307. FakeAnalytics *analytics = [[FakeAnalytics alloc]
  308. initWithEventHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
  309. XCTAssertEqualObjects(origin, kFIREventOriginFCM);
  310. XCTAssertEqualObjects(name, @"_cmp");
  311. XCTAssertEqualObjects(parameters, params);
  312. [expectation fulfill];
  313. expectation = nil;
  314. }];
  315. [FIRMessagingAnalytics logUserPropertyForConversionTracking:notification toAnalytics:analytics];
  316. [self waitForExpectationsWithTimeout:kAsyncTestTimout handler:nil];
  317. }
  318. - (void)testConversionTrackingUserProperty {
  319. // Notification contains "google.c.a.tc" key.
  320. NSDictionary *notification = @{
  321. @"aps" : @{@"alert" : @"to check the reporting format"},
  322. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  323. @"gcm.n.e" : @"1",
  324. @"google.c.a.c_l" : @"Hello World",
  325. @"google.c.a.c_id" : @"575315420755741863",
  326. @"google.c.a.e" : @"1",
  327. @"google.c.a.ts" : @"1522880044",
  328. @"google.c.a.udt" : @"0",
  329. @"google.c.a.m_l" : @"developer's customized label",
  330. @"google.c.a.tc" : @"1",
  331. @"from" : @"/topics/news",
  332. };
  333. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  334. FakeAnalytics *analytics = [[FakeAnalytics alloc]
  335. initWithUserPropertyHandler:^(NSString *origin, NSString *name, id value) {
  336. XCTAssertEqualObjects(origin, kFIREventOriginFCM);
  337. XCTAssertEqualObjects(name, @"_ln");
  338. XCTAssertEqualObjects(value, @"575315420755741863");
  339. [expectation fulfill];
  340. }];
  341. [FIRMessagingAnalytics logUserPropertyForConversionTracking:notification toAnalytics:analytics];
  342. [self waitForExpectationsWithTimeout:kAsyncTestTimout handler:nil];
  343. }
  344. - (void)testNoConversionTracking {
  345. // Notification contains "google.c.a.tc" key.
  346. NSDictionary *notification = @{
  347. @"aps" : @{@"alert" : @"to check the reporting format"},
  348. @"gcm.message_id" : @"0:1522880049414338%944841cd944841cd",
  349. @"gcm.n.e" : @"1",
  350. @"google.c.a.c_l" : @"Hello World",
  351. @"google.c.a.c_id" : @"575315420755741863",
  352. @"google.c.a.e" : @"1",
  353. @"google.c.a.ts" : @"1522880044",
  354. @"google.c.a.udt" : @"0",
  355. @"google.c.a.m_l" : @"developer's customized label",
  356. @"from" : @"/topics/news",
  357. };
  358. FakeAnalytics *analytics = [[FakeAnalytics alloc]
  359. initWithEventHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
  360. XCTAssertTrue(NO);
  361. }];
  362. [FIRMessagingAnalytics logUserPropertyForConversionTracking:notification toAnalytics:analytics];
  363. }
  364. - (void)testLogMessage {
  365. NSDictionary *notification = @{
  366. @"google.c.a.e" : @"1",
  367. };
  368. [FIRMessagingAnalytics logMessage:notification toAnalytics:nil];
  369. OCMVerify([self.logClassMock logEvent:OCMOCK_ANY withNotification:notification toAnalytics:nil]);
  370. }
  371. - (void)testLogOpenNotification {
  372. NSDictionary *notification = @{
  373. @"google.c.a.e" : @"1",
  374. };
  375. [FIRMessagingAnalytics logOpenNotification:notification toAnalytics:nil];
  376. OCMVerify([self.logClassMock logUserPropertyForConversionTracking:notification toAnalytics:nil]);
  377. OCMVerify([self.logClassMock logEvent:kFIRIEventNotificationOpen
  378. withNotification:notification
  379. toAnalytics:nil]);
  380. }
  381. @end
  382. #endif