FIRTraceTest.m 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  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. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  372. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  373. [trace start];
  374. [defaultCenter postNotificationName:UIApplicationDidEnterBackgroundNotification
  375. object:[UIApplication sharedApplication]];
  376. [defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification
  377. object:[UIApplication sharedApplication]];
  378. XCTAssertEqual(trace.backgroundTraceState, FPRTraceStateBackgroundAndForeground);
  379. [trace stop];
  380. }
  381. /** Validates the value of background state when trace is not started. */
  382. - (void)testValidTraceWithoutStart {
  383. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  384. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  385. [defaultCenter postNotificationName:UIApplicationDidEnterBackgroundNotification
  386. object:[UIApplication sharedApplication]];
  387. [defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification
  388. object:[UIApplication sharedApplication]];
  389. [trace stop];
  390. XCTAssertEqual(trace.backgroundTraceState, FPRTraceStateUnknown);
  391. }
  392. /** Validates the value of background state is available after trace is stopped. */
  393. - (void)testBackgroundStateAfterTraceStop {
  394. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  395. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  396. [trace start];
  397. [defaultCenter postNotificationName:UIApplicationDidEnterBackgroundNotification
  398. object:[UIApplication sharedApplication]];
  399. [defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification
  400. object:[UIApplication sharedApplication]];
  401. [trace stop];
  402. XCTAssertEqual(trace.backgroundTraceState, FPRTraceStateBackgroundAndForeground);
  403. }
  404. /** Validates that stages do not have any valid background state. */
  405. - (void)testValidTraceWithActiveStageHavingNoBackgroundState {
  406. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  407. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  408. [trace start];
  409. [trace startStageNamed:@"RandomStage"];
  410. [defaultCenter postNotificationName:UIApplicationDidEnterBackgroundNotification
  411. object:[UIApplication sharedApplication]];
  412. [defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification
  413. object:[UIApplication sharedApplication]];
  414. [trace stop];
  415. XCTAssertEqual(trace.stages.count, 1);
  416. FIRTrace *activeStage = [trace.stages lastObject];
  417. XCTAssertEqual(activeStage.backgroundTraceState, FPRTraceStateUnknown);
  418. }
  419. /** Validates that internal trace names allow the reserved prefix value. */
  420. - (void)testInternalTraceCreationWithInternalPrefix {
  421. FIRTrace *trace = [[FIRTrace alloc] initInternalTraceWithName:@"_Random"];
  422. XCTAssertNotNil(trace);
  423. NSString *metricName = @"_counter";
  424. [trace start];
  425. [trace startStageNamed:@"_1"];
  426. [trace incrementMetric:metricName byInt:5];
  427. [trace stop];
  428. XCTAssertEqual(trace.stages.count, 1);
  429. FIRTrace *stage1 = [trace.stages lastObject];
  430. XCTAssertEqual(stage1.name, @"_1");
  431. NSUInteger metricValue = [[trace.counters objectForKey:metricName] integerValue];
  432. XCTAssertEqual(metricValue, 5);
  433. }
  434. /** Validates if the metric is incremented if a trace is started but not stopped. */
  435. - (void)testTraceStartedNotStoppedIncrementsAMetric {
  436. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  437. [defaultCenter postNotificationName:UIWindowDidBecomeVisibleNotification
  438. object:[UIApplication sharedApplication]];
  439. [defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification
  440. object:[UIApplication sharedApplication]];
  441. FIRTrace *activeTrace = [FPRAppActivityTracker sharedInstance].activeTrace;
  442. NSNumber *metric = [activeTrace.counters objectForKey:kFPRAppCounterNameTraceNotStopped];
  443. NSInteger metricValue = [metric integerValue];
  444. __weak FIRTrace *weakReferencedTrace;
  445. @autoreleasepool {
  446. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  447. weakReferencedTrace = trace;
  448. [trace start];
  449. [trace startStageNamed:@"1"];
  450. }
  451. XCTestExpectation *expectation = [self expectationWithDescription:@"Expectation - Wait for 2s"];
  452. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),
  453. dispatch_get_main_queue(), ^{
  454. [expectation fulfill];
  455. XCTAssertNil(weakReferencedTrace);
  456. });
  457. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  458. NSNumber *updatedMetric = [activeTrace.counters objectForKey:kFPRAppCounterNameTraceNotStopped];
  459. NSInteger updatedMetricValue = [updatedMetric integerValue];
  460. XCTAssertEqual(updatedMetricValue - metricValue, 1);
  461. [defaultCenter postNotificationName:UIApplicationWillResignActiveNotification
  462. object:[UIApplication sharedApplication]];
  463. }
  464. /** Validates if the metric is not incremented if a trace is started and stopped. */
  465. - (void)testTraceStartedAndStoppedDoesNotIncrementAMetric {
  466. FIRTrace *activeTrace = [FPRAppActivityTracker sharedInstance].activeTrace;
  467. NSNumber *metric = [activeTrace.counters objectForKey:kFPRAppCounterNameTraceNotStopped];
  468. NSInteger metricValue = [metric integerValue];
  469. __weak FIRTrace *weakReferencedTrace;
  470. @autoreleasepool {
  471. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  472. weakReferencedTrace = trace;
  473. [trace start];
  474. [trace startStageNamed:@"1"];
  475. [trace stop];
  476. }
  477. NSNumber *updatedMetric = [activeTrace.counters objectForKey:kFPRAppCounterNameTraceNotStopped];
  478. NSInteger updatedMetricValue = [updatedMetric integerValue];
  479. XCTAssertEqual(updatedMetricValue - metricValue, 0);
  480. }
  481. #pragma mark - Custom attribute related testing
  482. /** Validates if setting a valid attribute before calling start works. */
  483. - (void)testSettingValidAttributeBeforeStart {
  484. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  485. [trace setValue:@"bar" forAttribute:@"foo"];
  486. XCTAssertEqual([trace valueForAttribute:@"foo"], @"bar");
  487. }
  488. /** Validates if setting a valid attribute works between start/stop works. */
  489. - (void)testSettingValidAttributeBetweenStartAndStop {
  490. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  491. [trace start];
  492. [trace setValue:@"bar" forAttribute:@"foo"];
  493. XCTAssertEqual([trace valueForAttribute:@"foo"], @"bar");
  494. [trace stop];
  495. }
  496. /** Validates if setting a valid attribute works after stop is a no-op. */
  497. - (void)testSettingValidAttributeBetweenAfterStop {
  498. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  499. [trace start];
  500. [trace stop];
  501. [trace setValue:@"bar" forAttribute:@"foo"];
  502. XCTAssertNil([trace valueForAttribute:@"foo"]);
  503. }
  504. /** Validates if attributes property access works. */
  505. - (void)testReadingAttributesFromProperty {
  506. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  507. XCTAssertNotNil(trace.attributes);
  508. XCTAssertEqual(trace.attributes.count, 0);
  509. [trace setValue:@"bar" forAttribute:@"foo"];
  510. NSDictionary<NSString *, NSString *> *attributes = trace.attributes;
  511. XCTAssertEqual(attributes.allKeys.count, 1);
  512. }
  513. /** Validates if attributes property is immutable. */
  514. - (void)testImmutablityOfAttributesProperty {
  515. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  516. [trace setValue:@"bar" forAttribute:@"foo"];
  517. NSMutableDictionary<NSString *, NSString *> *attributes =
  518. (NSMutableDictionary<NSString *, NSString *> *)trace.attributes;
  519. XCTAssertThrows([attributes setValue:@"bar1" forKey:@"foo"]);
  520. }
  521. /** Validates if updating attribute value works. */
  522. - (void)testUpdatingAttributeValue {
  523. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  524. [trace setValue:@"bar" forAttribute:@"foo"];
  525. [trace setValue:@"baz" forAttribute:@"foo"];
  526. XCTAssertEqual([trace valueForAttribute:@"foo"], @"baz");
  527. [trace setValue:@"qux" forAttribute:@"foo"];
  528. XCTAssertEqual([trace valueForAttribute:@"foo"], @"qux");
  529. }
  530. /** Validates if removing attributes work before call to start. */
  531. - (void)testRemovingAttributeBeforeStart {
  532. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  533. [trace setValue:@"bar" forAttribute:@"foo"];
  534. [trace removeAttribute:@"foo"];
  535. XCTAssertNil([trace valueForAttribute:@"foo"]);
  536. [trace removeAttribute:@"foo"];
  537. XCTAssertNil([trace valueForAttribute:@"foo"]);
  538. }
  539. /** Validates if removing attributes work between start and stop calls. */
  540. - (void)testRemovingAttributeBetweenStartStop {
  541. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  542. [trace setValue:@"bar" forAttribute:@"foo"];
  543. [trace start];
  544. [trace removeAttribute:@"foo"];
  545. [trace stop];
  546. XCTAssertNil([trace valueForAttribute:@"foo"]);
  547. }
  548. /** Validates if removing attributes is a no-op after stop. */
  549. - (void)testRemovingAttributeBetweenAfterStop {
  550. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  551. [trace setValue:@"bar" forAttribute:@"foo"];
  552. [trace start];
  553. [trace stop];
  554. [trace removeAttribute:@"foo"];
  555. XCTAssertEqual([trace valueForAttribute:@"foo"], @"bar");
  556. }
  557. /** Validates if removing non-existing attributes works. */
  558. - (void)testRemovingNonExistingAttribute {
  559. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  560. [trace removeAttribute:@"foo"];
  561. XCTAssertNil([trace valueForAttribute:@"foo"]);
  562. [trace removeAttribute:@"foo"];
  563. XCTAssertNil([trace valueForAttribute:@"foo"]);
  564. }
  565. /** Validates if using reserved prefix in attribute prefix will drop the attribute. */
  566. - (void)testAttributeNamePrefixSrtipped {
  567. NSArray<NSString *> *reservedPrefix = @[ @"firebase_", @"google_", @"ga_" ];
  568. [reservedPrefix enumerateObjectsUsingBlock:^(NSString *prefix, NSUInteger idx, BOOL *stop) {
  569. NSString *attributeName = [NSString stringWithFormat:@"%@name", prefix];
  570. NSString *attributeValue = @"value";
  571. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  572. [trace setValue:attributeValue forAttribute:attributeName];
  573. XCTAssertNil([trace valueForAttribute:attributeName]);
  574. }];
  575. }
  576. /** Validates if long attribute names gets dropped. */
  577. - (void)testMaxLengthForAttributeName {
  578. NSString *testName = [@"abc" stringByPaddingToLength:kFPRMaxAttributeNameLength + 1
  579. withString:@"-"
  580. startingAtIndex:0];
  581. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  582. [trace setValue:@"bar" forAttribute:testName];
  583. XCTAssertNil([trace valueForAttribute:testName]);
  584. }
  585. /** Validates if attribute names with illegal characters gets dropped. */
  586. - (void)testIllegalCharactersInAttributeName {
  587. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  588. [trace setValue:@"bar" forAttribute:@"foo_"];
  589. XCTAssertEqual([trace valueForAttribute:@"foo_"], @"bar");
  590. [trace setValue:@"bar" forAttribute:@"foo_$"];
  591. XCTAssertNil([trace valueForAttribute:@"foo_$"]);
  592. [trace setValue:@"bar" forAttribute:@"FOO_$"];
  593. XCTAssertNil([trace valueForAttribute:@"FOO_$"]);
  594. [trace setValue:@"bar" forAttribute:@"FOO_"];
  595. XCTAssertEqual([trace valueForAttribute:@"FOO_"], @"bar");
  596. }
  597. /** Validates if long attribute values gets truncated. */
  598. - (void)testMaxLengthForAttributeValue {
  599. NSString *testValue = [@"abc" stringByPaddingToLength:kFPRMaxAttributeValueLength + 1
  600. withString:@"-"
  601. startingAtIndex:0];
  602. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  603. [trace setValue:testValue forAttribute:@"foo"];
  604. XCTAssertNil([trace valueForAttribute:@"foo"]);
  605. }
  606. /** Validates if empty name or value of the attributes are getting dropped. */
  607. - (void)testAttributesWithEmptyValues {
  608. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  609. [trace setValue:@"" forAttribute:@"foo"];
  610. XCTAssertNil([trace valueForAttribute:@"foo"]);
  611. [trace setValue:@"bar" forAttribute:@""];
  612. XCTAssertNil([trace valueForAttribute:@""]);
  613. }
  614. /** Validates if the limit the maximum number of attributes work. */
  615. - (void)testMaximumNumberOfAttributes {
  616. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  617. for (int i = 0; i < kFPRMaxGlobalCustomAttributesCount; i++) {
  618. NSString *attributeName = [NSString stringWithFormat:@"dim%d", i];
  619. [trace setValue:@"bar" forAttribute:attributeName];
  620. XCTAssertEqual([trace valueForAttribute:attributeName], @"bar");
  621. }
  622. [trace setValue:@"bar" forAttribute:@"foo"];
  623. XCTAssertNil([trace valueForAttribute:@"foo"]);
  624. }
  625. /** Validates if removing old attributes and adding new attributes work. */
  626. - (void)testRemovingAndAddingAttributes {
  627. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  628. for (int i = 0; i < kFPRMaxGlobalCustomAttributesCount; i++) {
  629. NSString *attributeName = [NSString stringWithFormat:@"dim%d", i];
  630. [trace setValue:@"bar" forAttribute:attributeName];
  631. XCTAssertEqual([trace valueForAttribute:attributeName], @"bar");
  632. }
  633. [trace removeAttribute:@"dim1"];
  634. [trace setValue:@"bar" forAttribute:@"foo"];
  635. XCTAssertEqual([trace valueForAttribute:@"foo"], @"bar");
  636. }
  637. /** Validates if every trace contains a session Id. */
  638. - (void)testSessionId {
  639. [[FPRSessionManager sharedInstance] updateSessionId:@"testSessionId"];
  640. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  641. [trace start];
  642. [trace stop];
  643. XCTAssertNotNil(trace.sessions);
  644. XCTAssertTrue(trace.sessions.count > 0);
  645. }
  646. /** Validates if every trace contains multiple session Ids on changing app state. */
  647. - (void)testMultipleSessionIds {
  648. [[FPRSessionManager sharedInstance] updateSessionId:@"testSessionId"];
  649. FIRTrace *trace = [[FIRTrace alloc] initWithName:@"Random"];
  650. [trace start];
  651. [[FPRSessionManager sharedInstance] updateSessionId:@"testSessionId2"];
  652. [[FPRSessionManager sharedInstance] updateSessionId:@"testSessionId3"];
  653. XCTestExpectation *expectation = [self expectationWithDescription:@"Expectation - Wait for 2s"];
  654. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),
  655. dispatch_get_main_queue(), ^{
  656. [expectation fulfill];
  657. [trace stop];
  658. XCTAssertNotNil(trace.sessions);
  659. XCTAssertTrue(trace.sessions.count >= 2);
  660. });
  661. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  662. }
  663. @end