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