FIRTraceTest.m 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  1. // Copyright 2020 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 <GoogleUtilities/GULUserDefaults.h>
  16. #import "FirebasePerformance/Sources/AppActivity/FPRAppActivityTracker.h"
  17. #import "FirebasePerformance/Sources/AppActivity/FPRSessionManager.h"
  18. #import "FirebasePerformance/Sources/Common/FPRConstants.h"
  19. #import "FirebasePerformance/Sources/Configurations/FPRConfigurations+Private.h"
  20. #import "FirebasePerformance/Sources/Configurations/FPRConfigurations.h"
  21. #import "FirebasePerformance/Sources/Configurations/FPRRemoteConfigFlags+Private.h"
  22. #import "FirebasePerformance/Sources/Configurations/FPRRemoteConfigFlags.h"
  23. #import "FirebasePerformance/Sources/FPRClient.h"
  24. #import "FirebasePerformance/Sources/Public/FirebasePerformance/FIRPerformance.h"
  25. #import "FirebasePerformance/Sources/Public/FirebasePerformance/FIRTrace.h"
  26. #import "FirebasePerformance/Sources/Timer/FIRTrace+Internal.h"
  27. #import "FirebasePerformance/Sources/Timer/FIRTrace+Private.h"
  28. #import "FirebasePerformance/Tests/Unit/Configurations/FPRFakeRemoteConfig.h"
  29. #import "FirebasePerformance/Tests/Unit/FPRTestCase.h"
  30. #import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h"
  31. #import <OCMock/OCMock.h>
  32. @interface FIRTraceTest : FPRTestCase
  33. @end
  34. @implementation FIRTraceTest
  35. - (void)setUp {
  36. [super setUp];
  37. FIRPerformance *performance = [FIRPerformance sharedInstance];
  38. [performance setDataCollectionEnabled:YES];
  39. [[FPRClient sharedInstance] disableInstrumentation];
  40. }
  41. - (void)tearDown {
  42. [super tearDown];
  43. FIRPerformance *performance = [FIRPerformance sharedInstance];
  44. [performance setDataCollectionEnabled:NO];
  45. [[FPRClient sharedInstance] disableInstrumentation];
  46. }
  47. /** Validates that init with a valid name returns a trace. */
  48. - (void)testInit {
  49. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  50. XCTAssertNotNil(trace);
  51. }
  52. /** Validates that init with an empty name throws exception. */
  53. - (void)testInitWithEmptyName {
  54. XCTAssertThrows([[FIRTrace alloc] initWithName:@""]);
  55. }
  56. #pragma mark - Trace creation tests
  57. /** Validates if trace creation fails when SDK flag is disabled in remote config. */
  58. - (void)testTraceCreationWhenSDKFlagDisabled {
  59. FPRConfigurations *configurations = [FPRConfigurations sharedInstance];
  60. FPRFakeRemoteConfig *remoteConfig = [[FPRFakeRemoteConfig alloc] init];
  61. FPRRemoteConfigFlags *configFlags =
  62. [[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
  63. configFlags.appStartConfigFetchDelayInSeconds = 0.0;
  64. configurations.remoteConfigFlags = configFlags;
  65. NSData *valueData = [@"false" dataUsingEncoding:NSUTF8StringEncoding];
  66. FIRRemoteConfigValue *value =
  67. [[FIRRemoteConfigValue alloc] initWithData:valueData source:FIRRemoteConfigSourceRemote];
  68. [remoteConfig.configValues setObject:value forKey:@"fpr_enabled"];
  69. // Trigger the RC config fetch
  70. remoteConfig.lastFetchTime = nil;
  71. configFlags.appStartConfigFetchDelayInSeconds = 0.0;
  72. [configFlags update];
  73. XCTAssertNil([[FIRTrace alloc] initWithName:@"Random"]);
  74. }
  75. /** Validates if trace creation succeeds when SDK flag is enabled in remote config. */
  76. - (void)testTraceCreationWhenSDKFlagEnabled {
  77. FPRConfigurations *configurations = [FPRConfigurations sharedInstance];
  78. FPRFakeRemoteConfig *remoteConfig = [[FPRFakeRemoteConfig alloc] init];
  79. FPRRemoteConfigFlags *configFlags =
  80. [[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
  81. configurations.remoteConfigFlags = configFlags;
  82. GULUserDefaults *userDefaults = [[GULUserDefaults alloc] init];
  83. configFlags.userDefaults = userDefaults;
  84. NSString *configKey = [NSString stringWithFormat:@"%@.%@", kFPRConfigPrefix, @"fpr_enabled"];
  85. [userDefaults setObject:@(TRUE) forKey:configKey];
  86. XCTAssertNotNil([[FIRTrace alloc] initWithName:@"Random"]);
  87. }
  88. /** Validates if trace creation fails when SDK flag is enabled in remote config, but data collection
  89. * disabled. */
  90. - (void)testTraceCreationWhenSDKFlagEnabledWithDataCollectionDisabled {
  91. [[FIRPerformance sharedInstance] setDataCollectionEnabled:NO];
  92. FPRConfigurations *configurations = [FPRConfigurations sharedInstance];
  93. FPRFakeRemoteConfig *remoteConfig = [[FPRFakeRemoteConfig alloc] init];
  94. FPRRemoteConfigFlags *configFlags =
  95. [[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
  96. configurations.remoteConfigFlags = configFlags;
  97. NSData *valueData = [@"true" dataUsingEncoding:NSUTF8StringEncoding];
  98. FIRRemoteConfigValue *value =
  99. [[FIRRemoteConfigValue alloc] initWithData:valueData source:FIRRemoteConfigSourceRemote];
  100. [remoteConfig.configValues setObject:value forKey:@"fpr_enabled"];
  101. XCTAssertNil([[FIRTrace alloc] initWithName:@"Random"]);
  102. }
  103. #pragma mark - Stages related testing
  104. /** Validates that stages are created. */
  105. - (void)testStaging {
  106. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  107. [trace start];
  108. [trace startStageNamed:@"1"];
  109. [trace startStageNamed:@"2"];
  110. [trace stop];
  111. XCTAssertEqual(trace.stages.count, 2);
  112. }
  113. /** Validates that stages are not created without calling a start on the trace. */
  114. - (void)testStageWithoutStart {
  115. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  116. [trace startStageNamed:@"1"];
  117. XCTAssertEqual(trace.stages.count, 0);
  118. }
  119. /** Validates that stages are not created without calling a start on the trace, but calling stop. */
  120. - (void)testStageWithoutStartWithStop {
  121. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  122. [trace startStageNamed:@"1"];
  123. [trace stop];
  124. XCTAssertEqual(trace.stages.count, 0);
  125. }
  126. /** Validates that stages are not created after calling stop on the trace. */
  127. - (void)testStageAfterStop {
  128. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  129. [trace start];
  130. [trace startStageNamed:@"1"];
  131. [trace startStageNamed:@"2"];
  132. [trace stop];
  133. [trace startStageNamed:@"3"];
  134. XCTAssertEqual(trace.stages.count, 2);
  135. }
  136. /** Validates that stopping a stage does not trigger an event being sent to Fll */
  137. - (void)testStageStopDoesNotTriggerEventSend {
  138. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  139. id mock = [OCMockObject partialMockForObject:[FPRClient sharedInstance]];
  140. OCMStub([mock logTrace:[OCMArg any]]).andDo(nil);
  141. [trace start];
  142. [trace startStageNamed:@"1"];
  143. [[mock reject] logTrace:trace.activeStage];
  144. [trace startStageNamed:@"2"];
  145. [trace stop];
  146. }
  147. /** Validates that stopping a trace does trigger an event being sent to Fll. */
  148. - (void)testTraceStopDoesTriggerEventSend {
  149. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  150. id mock = [OCMockObject partialMockForObject:[FPRClient sharedInstance]];
  151. OCMStub([mock logTrace:[OCMArg any]]).andDo(nil);
  152. [trace start];
  153. [trace startStageNamed:@"1"];
  154. [trace startStageNamed:@"2"];
  155. [trace stop];
  156. OCMVerify([mock logTrace:trace]);
  157. }
  158. /** Validates that the name of the trace is dropped if its length is above max admissible length. */
  159. - (void)testNameLengthMax {
  160. NSString *testName = [@"abc" stringByPaddingToLength:kFPRMaxNameLength + 1
  161. withString:@"-"
  162. startingAtIndex:0];
  163. XCTAssertThrows([[FIRTrace alloc] initWithName:testName]);
  164. }
  165. /** Validates that the name cannot have a prefix of underscore. */
  166. - (void)testNamePrefixSrtipped {
  167. NSString *testName = [NSString stringWithFormat:@"%@test", kFPRInternalNamePrefix];
  168. XCTAssertThrows([[FIRTrace alloc] initWithName:testName]);
  169. }
  170. #pragma mark - Metric related testing
  171. /** Validates that metric with greater than max length is not created on setMetric. */
  172. - (void)testSetMetricNameLengthMax {
  173. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  174. NSString *testName = [@"abc" stringByPaddingToLength:kFPRMaxNameLength + 1
  175. withString:@"-"
  176. startingAtIndex:0];
  177. [trace start];
  178. [trace setIntValue:10 forMetric:testName];
  179. [trace stop];
  180. XCTAssertNil([trace.counters objectForKey:testName]);
  181. }
  182. /** Validates that metric with empty name is not created on setMetric. */
  183. - (void)testSetOrIncrementMetricNameLengthZero {
  184. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  185. NSString *testName = @"";
  186. [trace start];
  187. [trace setIntValue:10 forMetric:testName];
  188. [trace incrementMetric:testName byInt:10];
  189. [trace stop];
  190. XCTAssertNil([trace.counters objectForKey:testName]);
  191. }
  192. /** Validates that metrics are not set when a trace is not started. */
  193. - (void)testSetOrIncrementMetricWithoutStart {
  194. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  195. [trace setIntValue:10 forMetric:@"testing"];
  196. [trace incrementMetric:@"testing" byInt:10];
  197. [trace stop];
  198. XCTAssertNil([trace.counters objectForKey:@"testing"]);
  199. }
  200. /** Validates that calling get on a metric returns 0 if it hasn't been reviously set. */
  201. - (void)testGetMetricWhenSetHasntBeenCalledReturnsZero {
  202. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  203. int64_t metricValue = [trace valueForIntMetric:@"testing"];
  204. [trace stop];
  205. XCTAssertEqual(metricValue, 0);
  206. }
  207. /** Validates that calling get on a metric without calling set doesn't create a new metric. */
  208. - (void)testGetMetricWhenSetHasntBeenCalledDoesntCreateMetric {
  209. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  210. [trace valueForIntMetric:@"testing"];
  211. [trace stop];
  212. id metricValue = [trace.counters objectForKey:@"testing"];
  213. XCTAssertNil(metricValue);
  214. }
  215. /** Tests that calling set multiple times on a metric results in it holding just the last value. */
  216. - (void)testMultipleSetsOnAMetricResultInHoldingJustTheLastValue {
  217. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  218. [trace start];
  219. [trace setIntValue:10 forMetric:@"testing"];
  220. [trace setIntValue:100 forMetric:@"testing"];
  221. [trace stop];
  222. int64_t metricValue = [trace valueForIntMetric:@"testing"];
  223. XCTAssertEqual(metricValue, 100);
  224. }
  225. /** Validates that incrementing a metric that has been previously set increments previous value. */
  226. - (void)testIncrementingAfterSettingMetric {
  227. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  228. [trace start];
  229. [trace setIntValue:10 forMetric:@"testing"];
  230. [trace incrementMetric:@"testing" byInt:25];
  231. [trace stop];
  232. int64_t metricValue = [trace valueForIntMetric:@"testing"];
  233. XCTAssertEqual(metricValue, 35);
  234. }
  235. /** Validates that calling setMetric on a trace also sets it on the active stage. */
  236. - (void)testSetMetricCalledOnTraceAlsoSetsMetricOnStage {
  237. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  238. [trace start];
  239. [trace startStageNamed:@"stage 1"];
  240. [trace setIntValue:35 forMetric:@"testing"];
  241. [trace startStageNamed:@"stage 2"];
  242. [trace setIntValue:40 forMetric:@"testing"];
  243. [trace stop];
  244. XCTAssertEqual([trace valueForIntMetric:@"testing"], 40);
  245. for (FIRTrace *stage in trace.stages) {
  246. if ([stage.name isEqualToString:@"stage 1"]) {
  247. XCTAssertEqual([stage valueForIntMetric:@"testing"], 35);
  248. } else if ([stage.name isEqualToString:@"stage 2"]) {
  249. XCTAssertEqual([stage valueForIntMetric:@"testing"], 40);
  250. }
  251. }
  252. }
  253. /** Validates that deleting a metric deletes it. */
  254. - (void)testDeleteMetricDeletesAMetric {
  255. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  256. [trace start];
  257. [trace setIntValue:1 forMetric:@"testing"];
  258. [trace deleteMetric:@"testing"];
  259. [trace stop];
  260. XCTAssertNil(trace.counters[@"testing"]);
  261. }
  262. /** Validates that deleting a metric doesnt affect other metrics. */
  263. - (void)testDeleteMetricDoesntDeleteAnotherMetric {
  264. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  265. [trace start];
  266. [trace setIntValue:1 forMetric:@"testing"];
  267. [trace setIntValue:1 forMetric:@"testing2"];
  268. [trace deleteMetric:@"testing"];
  269. [trace stop];
  270. XCTAssertNil(trace.counters[@"testing"]);
  271. XCTAssertEqual([trace valueForIntMetric:@"testing2"], 1);
  272. }
  273. /** Validates that trying to delete a non-existent metric doesnt affect anything. */
  274. - (void)testDeletingMetricThatDoesntExistDoesntDoAnything {
  275. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  276. [trace start];
  277. [trace setIntValue:1 forMetric:@"testing"];
  278. [trace deleteMetric:@"testing2"];
  279. [trace stop];
  280. XCTAssertEqual([trace valueForIntMetric:@"testing"], 1);
  281. }
  282. /** Tests deleting a metric also deletes it from the active stage if it exists. */
  283. - (void)testDeleteMetricAlsoDeletesItFromActiveStage {
  284. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  285. [trace start];
  286. [trace startStageNamed:@"stage 1"];
  287. FIRTrace *activeStage = trace.activeStage;
  288. [trace setIntValue:1 forMetric:@"testing"];
  289. [trace setIntValue:1 forMetric:@"testing2"];
  290. [trace deleteMetric:@"testing"];
  291. [trace stop];
  292. XCTAssertEqual([trace valueForIntMetric:@"testing2"], 1);
  293. XCTAssertEqual([activeStage valueForIntMetric:@"testing2"], 1);
  294. XCTAssertNil(trace.counters[@"testing"]);
  295. XCTAssertNil(activeStage.counters[@"testing"]);
  296. }
  297. /** Tests that deleteMetric has no effect after the trace has been stopped. */
  298. - (void)testDeleteMetricDoesNothingAfterTraceHasBeenStopped {
  299. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  300. [trace start];
  301. [trace setIntValue:1 forMetric:@"testing"];
  302. [trace stop];
  303. [trace deleteMetric:@"testing"];
  304. XCTAssertEqual([trace valueForIntMetric:@"testing"], 1);
  305. }
  306. #pragma mark - Metrics related testing
  307. /** Validates that counters are incremented. */
  308. - (void)testMetricNameLengthMax {
  309. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  310. NSString *testName = [@"abc" stringByPaddingToLength:kFPRMaxNameLength + 1
  311. withString:@"-"
  312. startingAtIndex:0];
  313. [trace start];
  314. [trace incrementMetric:testName byInt:5];
  315. [trace stop];
  316. XCTAssertNil([trace.counters objectForKey:testName]);
  317. }
  318. /** Validates that traces could start with a custom start time. */
  319. - (void)testStartTraceWithStartTimeAndStageDefined {
  320. NSDate *traceStartTime = [NSDate date];
  321. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"testTrace"];
  322. [trace startWithStartTime:traceStartTime];
  323. XCTAssertEqual(trace.startTimeSinceEpoch, [traceStartTime timeIntervalSince1970]);
  324. [trace startStageNamed:@"testStage" startTime:traceStartTime];
  325. [trace stop];
  326. XCTAssertEqual(trace.stages.count, 1);
  327. FIRTrace *stage = trace.stages.lastObject;
  328. XCTAssertEqual(stage.startTimeSinceEpoch, [traceStartTime timeIntervalSince1970]);
  329. }
  330. - (void)testMetrics {
  331. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  332. [trace start];
  333. [trace incrementMetric:@"testing" byInt:3];
  334. [trace incrementMetric:@"testing" byInt:2];
  335. [trace stop];
  336. NSUInteger metricValue = [[trace.counters objectForKey:@"testing"] integerValue];
  337. XCTAssertEqual(metricValue, 5);
  338. }
  339. /** Validates that metrics are not incremented when a trace is not started. */
  340. - (void)testMetricsWithoutStart {
  341. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  342. [trace incrementMetric:@"testing" byInt:3];
  343. [trace incrementMetric:@"testing" byInt:2];
  344. [trace stop];
  345. NSUInteger metricValue = [[trace.counters objectForKey:@"testing"] integerValue];
  346. XCTAssertEqual(metricValue, 0);
  347. }
  348. /** Validates that trace without complete data is invalid. */
  349. - (void)testInvalidTraceValidationCheck {
  350. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  351. [trace stop];
  352. XCTAssertFalse([trace isCompleteAndValid]);
  353. }
  354. /** Validates that valid traces with stages and metrics are marked as valid. */
  355. - (void)testValidTraceWithStageAndMetrics {
  356. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  357. [trace start];
  358. [trace incrementMetric:@"counter 1" byInt:1];
  359. [trace incrementMetric:@"counter 2" byInt:1];
  360. [trace startStageNamed:@"1"];
  361. [trace incrementMetric:@"counter 1" byInt:1];
  362. [trace incrementMetric:@"counter 2" byInt:1];
  363. [trace startStageNamed:@"2"];
  364. [trace incrementMetric:@"counter 1" byInt:1];
  365. [trace incrementMetric:@"counter 2" byInt:1];
  366. [trace stop];
  367. XCTAssertTrue([trace isCompleteAndValid]);
  368. }
  369. /** Validates the value of background state when the app is backgrounded. */
  370. - (void)testValidTraceWithBackgrounding {
  371. XCTestExpectation *expectation = [self expectationWithDescription:@"Application state change"];
  372. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  373. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  374. [trace start];
  375. dispatch_async(dispatch_get_main_queue(), ^{
  376. [defaultCenter postNotificationName:UIApplicationDidEnterBackgroundNotification
  377. object:[UIApplication sharedApplication]];
  378. [defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification
  379. object:[UIApplication sharedApplication]];
  380. [expectation fulfill];
  381. });
  382. [self waitForExpectationsWithTimeout:5.0
  383. handler:^(NSError *_Nullable error) {
  384. if (error) {
  385. XCTFail(@"Expectation failed with error: %@", error);
  386. } else {
  387. XCTAssertEqual(trace.backgroundTraceState,
  388. FPRTraceStateBackgroundAndForeground);
  389. [trace stop];
  390. }
  391. }];
  392. }
  393. /** Validates the value of background state when trace is not started. */
  394. - (void)testValidTraceWithoutStart {
  395. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  396. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  397. [defaultCenter postNotificationName:UIApplicationDidEnterBackgroundNotification
  398. object:[UIApplication sharedApplication]];
  399. [defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification
  400. object:[UIApplication sharedApplication]];
  401. [trace stop];
  402. XCTAssertEqual(trace.backgroundTraceState, FPRTraceStateUnknown);
  403. }
  404. /** Validates the value of background state is available after trace is stopped. */
  405. - (void)testBackgroundStateAfterTraceStop {
  406. XCTestExpectation *expectation = [self expectationWithDescription:@"Application state change"];
  407. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  408. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  409. [trace start];
  410. dispatch_async(dispatch_get_main_queue(), ^{
  411. [defaultCenter postNotificationName:UIApplicationDidEnterBackgroundNotification
  412. object:[UIApplication sharedApplication]];
  413. [defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification
  414. object:[UIApplication sharedApplication]];
  415. [expectation fulfill];
  416. });
  417. [self waitForExpectationsWithTimeout:5.0
  418. handler:^(NSError *_Nullable error) {
  419. if (error) {
  420. XCTFail(@"Expectation failed with error: %@", error);
  421. } else {
  422. [trace stop];
  423. XCTAssertEqual(trace.backgroundTraceState,
  424. FPRTraceStateBackgroundAndForeground);
  425. }
  426. }];
  427. }
  428. /** Validates that stages do not have any valid background state. */
  429. - (void)testValidTraceWithActiveStageHavingNoBackgroundState {
  430. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  431. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  432. [trace start];
  433. [trace startStageNamed:@"RandomStage"];
  434. [defaultCenter postNotificationName:UIApplicationDidEnterBackgroundNotification
  435. object:[UIApplication sharedApplication]];
  436. [defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification
  437. object:[UIApplication sharedApplication]];
  438. [trace stop];
  439. XCTAssertEqual(trace.stages.count, 1);
  440. FIRTrace *activeStage = [trace.stages lastObject];
  441. XCTAssertEqual(activeStage.backgroundTraceState, FPRTraceStateUnknown);
  442. }
  443. /** Validates that internal trace names allow the reserved prefix value. */
  444. - (void)testInternalTraceCreationWithInternalPrefix {
  445. FIRTrace *trace = [[FIRTrace alloc] initInternalTraceWithName:@"_Random"];
  446. XCTAssertNotNil(trace);
  447. NSString *metricName = @"_counter";
  448. [trace start];
  449. [trace startStageNamed:@"_1"];
  450. [trace incrementMetric:metricName byInt:5];
  451. [trace stop];
  452. XCTAssertEqual(trace.stages.count, 1);
  453. FIRTrace *stage1 = [trace.stages lastObject];
  454. XCTAssertEqual(stage1.name, @"_1");
  455. NSUInteger metricValue = [[trace.counters objectForKey:metricName] integerValue];
  456. XCTAssertEqual(metricValue, 5);
  457. }
  458. /** Validates if the metric is incremented if a trace is started but not stopped. */
  459. - (void)testTraceStartedNotStoppedIncrementsAMetric {
  460. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  461. [defaultCenter postNotificationName:UIWindowDidBecomeVisibleNotification
  462. object:[UIApplication sharedApplication]];
  463. [defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification
  464. object:[UIApplication sharedApplication]];
  465. FIRTrace *activeTrace = [FPRAppActivityTracker sharedInstance].activeTrace;
  466. NSNumber *metric = [activeTrace.counters objectForKey:kFPRAppCounterNameTraceNotStopped];
  467. NSInteger metricValue = [metric integerValue];
  468. __weak FIRTrace *weakReferencedTrace;
  469. @autoreleasepool {
  470. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  471. weakReferencedTrace = trace;
  472. [trace start];
  473. [trace startStageNamed:@"1"];
  474. }
  475. XCTestExpectation *expectation = [self expectationWithDescription:@"Expectation - Wait for 2s"];
  476. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),
  477. dispatch_get_main_queue(), ^{
  478. [expectation fulfill];
  479. XCTAssertNil(weakReferencedTrace);
  480. });
  481. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  482. NSNumber *updatedMetric = [activeTrace.counters objectForKey:kFPRAppCounterNameTraceNotStopped];
  483. NSInteger updatedMetricValue = [updatedMetric integerValue];
  484. XCTAssertEqual(updatedMetricValue - metricValue, 1);
  485. [defaultCenter postNotificationName:UIApplicationWillResignActiveNotification
  486. object:[UIApplication sharedApplication]];
  487. }
  488. /** Validates if the metric is not incremented if a trace is started and stopped. */
  489. - (void)testTraceStartedAndStoppedDoesNotIncrementAMetric {
  490. FIRTrace *activeTrace = [FPRAppActivityTracker sharedInstance].activeTrace;
  491. NSNumber *metric = [activeTrace.counters objectForKey:kFPRAppCounterNameTraceNotStopped];
  492. NSInteger metricValue = [metric integerValue];
  493. __weak FIRTrace *weakReferencedTrace;
  494. @autoreleasepool {
  495. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  496. weakReferencedTrace = trace;
  497. [trace start];
  498. [trace startStageNamed:@"1"];
  499. [trace stop];
  500. }
  501. NSNumber *updatedMetric = [activeTrace.counters objectForKey:kFPRAppCounterNameTraceNotStopped];
  502. NSInteger updatedMetricValue = [updatedMetric integerValue];
  503. XCTAssertEqual(updatedMetricValue - metricValue, 0);
  504. }
  505. #pragma mark - Custom attribute related testing
  506. /** Validates if setting a valid attribute before calling start works. */
  507. - (void)testSettingValidAttributeBeforeStart {
  508. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  509. [trace setValue:@"bar" forAttribute:@"foo"];
  510. XCTAssertEqual([trace valueForAttribute:@"foo"], @"bar");
  511. }
  512. /** Validates if setting a valid attribute works between start/stop works. */
  513. - (void)testSettingValidAttributeBetweenStartAndStop {
  514. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  515. [trace start];
  516. [trace setValue:@"bar" forAttribute:@"foo"];
  517. XCTAssertEqual([trace valueForAttribute:@"foo"], @"bar");
  518. [trace stop];
  519. }
  520. /** Validates if setting a valid attribute works after stop is a no-op. */
  521. - (void)testSettingValidAttributeBetweenAfterStop {
  522. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  523. [trace start];
  524. [trace stop];
  525. [trace setValue:@"bar" forAttribute:@"foo"];
  526. XCTAssertNil([trace valueForAttribute:@"foo"]);
  527. }
  528. /** Validates if attributes property access works. */
  529. - (void)testReadingAttributesFromProperty {
  530. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  531. XCTAssertNotNil(trace.attributes);
  532. XCTAssertEqual(trace.attributes.count, 0);
  533. [trace setValue:@"bar" forAttribute:@"foo"];
  534. NSDictionary<NSString *, NSString *> *attributes = trace.attributes;
  535. XCTAssertEqual(attributes.allKeys.count, 1);
  536. }
  537. /** Validates if attributes property is immutable. */
  538. - (void)testImmutablityOfAttributesProperty {
  539. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  540. [trace setValue:@"bar" forAttribute:@"foo"];
  541. NSMutableDictionary<NSString *, NSString *> *attributes =
  542. (NSMutableDictionary<NSString *, NSString *> *)trace.attributes;
  543. XCTAssertThrows([attributes setValue:@"bar1" forKey:@"foo"]);
  544. }
  545. /** Validates if updating attribute value works. */
  546. - (void)testUpdatingAttributeValue {
  547. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  548. [trace setValue:@"bar" forAttribute:@"foo"];
  549. [trace setValue:@"baz" forAttribute:@"foo"];
  550. XCTAssertEqual([trace valueForAttribute:@"foo"], @"baz");
  551. [trace setValue:@"qux" forAttribute:@"foo"];
  552. XCTAssertEqual([trace valueForAttribute:@"foo"], @"qux");
  553. }
  554. /** Validates if removing attributes work before call to start. */
  555. - (void)testRemovingAttributeBeforeStart {
  556. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  557. [trace setValue:@"bar" forAttribute:@"foo"];
  558. [trace removeAttribute:@"foo"];
  559. XCTAssertNil([trace valueForAttribute:@"foo"]);
  560. [trace removeAttribute:@"foo"];
  561. XCTAssertNil([trace valueForAttribute:@"foo"]);
  562. }
  563. /** Validates if removing attributes work between start and stop calls. */
  564. - (void)testRemovingAttributeBetweenStartStop {
  565. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  566. [trace setValue:@"bar" forAttribute:@"foo"];
  567. [trace start];
  568. [trace removeAttribute:@"foo"];
  569. [trace stop];
  570. XCTAssertNil([trace valueForAttribute:@"foo"]);
  571. }
  572. /** Validates if removing attributes is a no-op after stop. */
  573. - (void)testRemovingAttributeBetweenAfterStop {
  574. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  575. [trace setValue:@"bar" forAttribute:@"foo"];
  576. [trace start];
  577. [trace stop];
  578. [trace removeAttribute:@"foo"];
  579. XCTAssertEqual([trace valueForAttribute:@"foo"], @"bar");
  580. }
  581. /** Validates if removing non-existing attributes works. */
  582. - (void)testRemovingNonExistingAttribute {
  583. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  584. [trace removeAttribute:@"foo"];
  585. XCTAssertNil([trace valueForAttribute:@"foo"]);
  586. [trace removeAttribute:@"foo"];
  587. XCTAssertNil([trace valueForAttribute:@"foo"]);
  588. }
  589. /** Validates if using reserved prefix in attribute prefix will drop the attribute. */
  590. - (void)testAttributeNamePrefixSrtipped {
  591. NSArray<NSString *> *reservedPrefix = @[ @"firebase_", @"google_", @"ga_" ];
  592. [reservedPrefix enumerateObjectsUsingBlock:^(NSString *prefix, NSUInteger idx, BOOL *stop) {
  593. NSString *attributeName = [NSString stringWithFormat:@"%@name", prefix];
  594. NSString *attributeValue = @"value";
  595. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  596. [trace setValue:attributeValue forAttribute:attributeName];
  597. XCTAssertNil([trace valueForAttribute:attributeName]);
  598. }];
  599. }
  600. /** Validates if long attribute names gets dropped. */
  601. - (void)testMaxLengthForAttributeName {
  602. NSString *testName = [@"abc" stringByPaddingToLength:kFPRMaxAttributeNameLength + 1
  603. withString:@"-"
  604. startingAtIndex:0];
  605. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  606. [trace setValue:@"bar" forAttribute:testName];
  607. XCTAssertNil([trace valueForAttribute:testName]);
  608. }
  609. /** Validates if attribute names with illegal characters gets dropped. */
  610. - (void)testIllegalCharactersInAttributeName {
  611. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  612. [trace setValue:@"bar" forAttribute:@"foo_"];
  613. XCTAssertEqual([trace valueForAttribute:@"foo_"], @"bar");
  614. [trace setValue:@"bar" forAttribute:@"foo_$"];
  615. XCTAssertNil([trace valueForAttribute:@"foo_$"]);
  616. [trace setValue:@"bar" forAttribute:@"FOO_$"];
  617. XCTAssertNil([trace valueForAttribute:@"FOO_$"]);
  618. [trace setValue:@"bar" forAttribute:@"FOO_"];
  619. XCTAssertEqual([trace valueForAttribute:@"FOO_"], @"bar");
  620. }
  621. /** Validates if long attribute values gets truncated. */
  622. - (void)testMaxLengthForAttributeValue {
  623. NSString *testValue = [@"abc" stringByPaddingToLength:kFPRMaxAttributeValueLength + 1
  624. withString:@"-"
  625. startingAtIndex:0];
  626. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  627. [trace setValue:testValue forAttribute:@"foo"];
  628. XCTAssertNil([trace valueForAttribute:@"foo"]);
  629. }
  630. /** Validates if empty name or value of the attributes are getting dropped. */
  631. - (void)testAttributesWithEmptyValues {
  632. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  633. [trace setValue:@"" forAttribute:@"foo"];
  634. XCTAssertNil([trace valueForAttribute:@"foo"]);
  635. [trace setValue:@"bar" forAttribute:@""];
  636. XCTAssertNil([trace valueForAttribute:@""]);
  637. }
  638. /** Validates if the limit the maximum number of attributes work. */
  639. - (void)testMaximumNumberOfAttributes {
  640. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  641. for (int i = 0; i < kFPRMaxGlobalCustomAttributesCount; i++) {
  642. NSString *attributeName = [NSString stringWithFormat:@"dim%d", i];
  643. [trace setValue:@"bar" forAttribute:attributeName];
  644. XCTAssertEqual([trace valueForAttribute:attributeName], @"bar");
  645. }
  646. [trace setValue:@"bar" forAttribute:@"foo"];
  647. XCTAssertNil([trace valueForAttribute:@"foo"]);
  648. }
  649. /** Validates if removing old attributes and adding new attributes work. */
  650. - (void)testRemovingAndAddingAttributes {
  651. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  652. for (int i = 0; i < kFPRMaxGlobalCustomAttributesCount; i++) {
  653. NSString *attributeName = [NSString stringWithFormat:@"dim%d", i];
  654. [trace setValue:@"bar" forAttribute:attributeName];
  655. XCTAssertEqual([trace valueForAttribute:attributeName], @"bar");
  656. }
  657. [trace removeAttribute:@"dim1"];
  658. [trace setValue:@"bar" forAttribute:@"foo"];
  659. XCTAssertEqual([trace valueForAttribute:@"foo"], @"bar");
  660. }
  661. /** Validates if every trace contains a session Id. */
  662. - (void)testSessionId {
  663. [[FPRSessionManager sharedInstance] updateSessionId:@"testSessionId"];
  664. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  665. [trace start];
  666. [trace stop];
  667. XCTAssertNotNil(trace.sessions);
  668. XCTAssertTrue(trace.sessions.count > 0);
  669. }
  670. /** Validates if every trace contains multiple session Ids on changing app state. */
  671. - (void)testMultipleSessionIds {
  672. [[FPRSessionManager sharedInstance] updateSessionId:@"testSessionId"];
  673. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  674. [trace start];
  675. [[FPRSessionManager sharedInstance] updateSessionId:@"testSessionId2"];
  676. [[FPRSessionManager sharedInstance] updateSessionId:@"testSessionId3"];
  677. XCTestExpectation *expectation = [self expectationWithDescription:@"Expectation - Wait for 2s"];
  678. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),
  679. dispatch_get_main_queue(), ^{
  680. [expectation fulfill];
  681. [trace stop];
  682. XCTAssertNotNil(trace.sessions);
  683. XCTAssertTrue(trace.sessions.count >= 2);
  684. });
  685. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  686. }
  687. @end