| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594 |
- // Copyright 2021 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #import <Foundation/Foundation.h>
- #import <XCTest/XCTest.h>
- #import "Crashlytics/Crashlytics/Controllers/FIRCLSMetricKitManager.h"
- #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
- #if CLS_METRICKIT_SUPPORTED
- #if __has_include(<FBLPromises/FBLPromises.h>)
- #import <FBLPromises/FBLPromises.h>
- #else
- #import "FBLPromises.h"
- #endif
- #include "Crashlytics/Crashlytics/Components/FIRCLSContext.h"
- #import "Crashlytics/Crashlytics/Controllers/FIRCLSManagerData.h"
- #import "Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h"
- #import "Crashlytics/Crashlytics/Models/FIRCLSExecutionIdentifierModel.h"
- #import "Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h"
- #import "Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.h"
- #import "Crashlytics/UnitTests/Mocks/FIRAppFake.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockReportManager.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockReportUploader.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockSettings.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSTempMockFileManager.h"
- #import "Crashlytics/UnitTests/Mocks/FIRMockGDTCoreTransport.h"
- #import "Crashlytics/UnitTests/Mocks/FIRMockInstallations.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockExistingReportManager.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockMXCPUExceptionDiagnostic.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockMXCallStackTree.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockMXCrashDiagnostic.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockMXDiagnosticPayload.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockMXDiskWriteExceptionDiagnostic.h"
- #import "Crashlytics/UnitTests/Mocks/FIRCLSMockMXHangDiagnostic.h"
- #define TEST_GOOGLE_APP_ID (@"1:632950151350:ios:d5b0d08d4f00f4b1")
- API_AVAILABLE(ios(14))
- @interface FIRCLSMetricKitManagerTests : XCTestCase
- @property(nonatomic, strong) FIRCLSMockReportManager *reportManager;
- @property(nonatomic, strong) FIRCLSMetricKitManager *metricKitManager;
- @property(nonatomic, strong) FIRCLSMockSettings *mockSettings;
- @property(nonatomic, strong) FIRCLSManagerData *managerData;
- @property(nonatomic, strong) FIRCLSMockReportUploader *mockReportUploader;
- @property(nonatomic, strong) FIRCLSTempMockFileManager *fileManager;
- @property(nonatomic, strong) FIRCLSMockExistingReportManager *existingReportManager;
- @property(nonatomic, strong) FIRCLSDataCollectionArbiter *dataArbiter;
- @property(nonatomic, strong) FIRCLSApplicationIdentifierModel *appIDModel;
- @property(nonatomic, strong) NSDate *beginTime;
- @property(nonatomic, strong) NSDate *endTime;
- @end
- @implementation FIRCLSMetricKitManagerTests
- - (void)setUp {
- [super setUp];
- FIRSetLoggerLevel(FIRLoggerLevelMax);
- FIRCLSContextBaseInit();
- id fakeApp = [[FIRAppFake alloc] init];
- self.dataArbiter = [[FIRCLSDataCollectionArbiter alloc] initWithApp:fakeApp withAppInfo:@{}];
- self.fileManager = [[FIRCLSTempMockFileManager alloc] init];
- // Delete cached settings
- [self.fileManager removeItemAtPath:_fileManager.settingsFilePath];
- FIRMockInstallations *iid = [[FIRMockInstallations alloc] initWithFID:@"test_token"];
- FIRMockGDTCORTransport *mockGoogleTransport =
- [[FIRMockGDTCORTransport alloc] initWithMappingID:@"id" transformers:nil target:0];
- FIRCLSApplicationIdentifierModel *appIDModel = [[FIRCLSApplicationIdentifierModel alloc] init];
- FIRCLSMockSettings *mockSettings =
- [[FIRCLSMockSettings alloc] initWithFileManager:self.fileManager appIDModel:appIDModel];
- // Allow nil values only in tests
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wnonnull"
- _managerData = [[FIRCLSManagerData alloc] initWithGoogleAppID:TEST_GOOGLE_APP_ID
- googleTransport:mockGoogleTransport
- installations:iid
- analytics:nil
- fileManager:self.fileManager
- dataArbiter:self.dataArbiter
- settings:mockSettings
- onDemandModel:nil];
- #pragma clang diagnostic pop
- self.mockReportUploader = [[FIRCLSMockReportUploader alloc] initWithManagerData:self.managerData];
- self.existingReportManager =
- [[FIRCLSMockExistingReportManager alloc] initWithManagerData:self.managerData
- reportUploader:self.mockReportUploader];
- [self.fileManager createReportDirectories];
- [self.fileManager
- setupNewPathForExecutionIdentifier:self.managerData.executionIDModel.executionID];
- self.metricKitManager =
- [[FIRCLSMetricKitManager alloc] initWithManagerData:self.managerData
- existingReportManager:self.existingReportManager
- fileManager:self.fileManager];
- self.beginTime = [NSDate date];
- self.endTime = [NSDate dateWithTimeIntervalSinceNow:1];
- }
- - (void)tearDown {
- self.existingReportManager = nil;
- if ([[NSFileManager defaultManager] fileExistsAtPath:[self.fileManager rootPath]]) {
- assert([self.fileManager removeItemAtPath:[self.fileManager rootPath]]);
- }
- FIRCLSContextBaseDeinit();
- [super tearDown];
- }
- #pragma mark - Diagnostic Creation Helpers
- - (FIRCLSMockMXCallStackTree *)createMockCallStackTree {
- NSString *callStackTreeString =
- @"{\n \"callStacks\" : [\n {\n \"threadAttributed\" : true,\n "
- @"\"callStackRootFrames\" : [\n {\n \"binaryUUID\" : "
- @"\"6387F46B-BE42-4575-8BFA-782CAAE676AA\",\n \"offsetIntoBinaryTextSegment\" : "
- @"123,\n \"sampleCount\" : 20,\n \"binaryName\" : \"testBinaryName\",\n "
- @" \"address\" : 74565\n }\n ]\n }\n ],\n \"callStackPerThread\" : "
- @"true\n}";
- return [[FIRCLSMockMXCallStackTree alloc] initWithStringData:callStackTreeString];
- }
- - (FIRCLSMockMXMetadata *)createMockMetadata {
- return [[FIRCLSMockMXMetadata alloc] initWithRegionFormat:@"US"
- osVersion:@"iPhone OS 15.0 (19A5281j)"
- deviceType:@"iPhone9,1"
- applicationBuildVersion:@"1"
- platformArchitecture:@"arm64"];
- }
- - (FIRCLSMockMXCrashDiagnostic *)createCrashDiagnostic {
- return [[FIRCLSMockMXCrashDiagnostic alloc]
- initWithCallStackTree:[self createMockCallStackTree]
- terminationReason:@"Namespace SIGNAL, Code 0xb"
- virtualMemoryRegionInfo:
- @"0 is not in any region. Bytes before following region: 4000000000 REGION TYPE "
- @" START - END [ VSIZE] PRT\\/MAX SHRMOD REGION DETAIL UNUSED "
- @"SPACE AT START ---> __TEXT 0000000000000000-0000000000000000 [ 32K] "
- @"r-x\\/r-x SM=COW ...pp\\/Test"
- exceptionType:@6
- exceptionCode:@0
- signal:@11
- metaData:[self createMockMetadata]
- applicationVersion:@"1"];
- }
- - (FIRCLSMockMXHangDiagnostic *)createHangDiagnostic {
- return [[FIRCLSMockMXHangDiagnostic alloc]
- initWithCallStackTree:[self createMockCallStackTree]
- hangDuration:[[NSMeasurement alloc] initWithDoubleValue:4.0
- unit:NSUnitDuration.seconds]
- metaData:[self createMockMetadata]
- applicationVersion:@"1"];
- }
- - (FIRCLSMockMXCPUExceptionDiagnostic *)createCPUExceptionDiagnostic {
- return [[FIRCLSMockMXCPUExceptionDiagnostic alloc]
- initWithCallStackTree:[self createMockCallStackTree]
- totalCPUTime:[[NSMeasurement alloc] initWithDoubleValue:1.0
- unit:NSUnitDuration.seconds]
- totalSampledTime:[[NSMeasurement alloc] initWithDoubleValue:2.0
- unit:NSUnitDuration.seconds]
- metaData:[self createMockMetadata]
- applicationVersion:@"1"];
- }
- - (FIRCLSMockMXDiskWriteExceptionDiagnostic *)createDiskWriteExcptionDiagnostic {
- return [[FIRCLSMockMXDiskWriteExceptionDiagnostic alloc]
- initWithCallStackTree:[self createMockCallStackTree]
- totalWritesCaused:[[NSMeasurement alloc] initWithDoubleValue:24.0
- unit:NSUnitDuration.seconds]
- metaData:[self createMockMetadata]
- applicationVersion:@"1"];
- }
- - (FIRCLSMockMXDiagnosticPayload *)createCrashDiagnosticPayload {
- NSDictionary *diagnostics = @{@"crashes" : @[ [self createCrashDiagnostic] ]};
- return [[FIRCLSMockMXDiagnosticPayload alloc] initWithDiagnostics:diagnostics
- timeStampBegin:self.beginTime
- timeStampEnd:self.endTime
- applicationVersion:@"1"];
- }
- - (FIRCLSMockMXDiagnosticPayload *)createHangDiagnosticPayload {
- NSDictionary *diagnostics = @{@"hangs" : @[ [self createHangDiagnostic] ]};
- return [[FIRCLSMockMXDiagnosticPayload alloc] initWithDiagnostics:diagnostics
- timeStampBegin:self.beginTime
- timeStampEnd:self.endTime
- applicationVersion:@"1"];
- }
- - (FIRCLSMockMXDiagnosticPayload *)createCPUExceptionDiagnosticPayload {
- NSDictionary *diagnostics =
- @{@"cpuExceptionDiagnostics" : @[ [self createCPUExceptionDiagnostic] ]};
- return [[FIRCLSMockMXDiagnosticPayload alloc] initWithDiagnostics:diagnostics
- timeStampBegin:self.beginTime
- timeStampEnd:self.endTime
- applicationVersion:@"1"];
- }
- - (FIRCLSMockMXDiagnosticPayload *)createDiskWriteExceptionDiagnosticPayload {
- NSDictionary *diagnostics =
- @{@"diskWriteExceptionDiagnostics" : @[ [self createDiskWriteExcptionDiagnostic] ]};
- return [[FIRCLSMockMXDiagnosticPayload alloc] initWithDiagnostics:diagnostics
- timeStampBegin:self.beginTime
- timeStampEnd:self.endTime
- applicationVersion:@"1"];
- }
- - (FIRCLSMockMXDiagnosticPayload *)createFullDiagnosticPayload {
- NSDictionary *diagnostics = @{
- @"crashes" : @[ [self createCrashDiagnostic] ],
- @"hangs" : @[ [self createHangDiagnostic] ],
- @"cpuExceptionDiagnostics" : @[ [self createCPUExceptionDiagnostic] ],
- @"diskWriteExceptionDiagnostics" : @[ [self createDiskWriteExcptionDiagnostic] ]
- };
- return [[FIRCLSMockMXDiagnosticPayload alloc] initWithDiagnostics:diagnostics
- timeStampBegin:self.beginTime
- timeStampEnd:self.endTime
- applicationVersion:@"1"];
- }
- - (FIRCLSMockMXDiagnosticPayload *)createEmptyDiagnosticPayload {
- NSDictionary *diagnostics = @{@"should" : @"be empty"};
- return [[FIRCLSMockMXDiagnosticPayload alloc] initWithDiagnostics:diagnostics
- timeStampBegin:self.beginTime
- timeStampEnd:self.endTime
- applicationVersion:@"1"];
- }
- - (FIRCLSMockMXDiagnosticPayload *)createDiagnosticPayloadWithMultipleCrashes {
- NSDictionary *diagnostics = @{
- @"crashes" : @[
- [self createCrashDiagnostic], [self createCrashDiagnostic], [self createCrashDiagnostic]
- ]
- };
- return [[FIRCLSMockMXDiagnosticPayload alloc] initWithDiagnostics:diagnostics
- timeStampBegin:self.beginTime
- timeStampEnd:self.endTime
- applicationVersion:@"1"];
- }
- - (void)checkMetadata:(NSDictionary *)metadata andThreads:(NSDictionary *)threads {
- XCTAssertNotNil(metadata, "MetricKit event should write metadata to file.");
- XCTAssertNotNil(threads, "MetricKit event should write threads to file.");
- XCTAssertTrue([[metadata objectForKey:@"appBuildVersion"] isEqualToString:@"1"]);
- XCTAssertTrue(
- [[metadata objectForKey:@"osVersion"] isEqualToString:@"iPhone OS 15.0 (19A5281j)"]);
- XCTAssertTrue([[metadata objectForKey:@"regionFormat"] isEqualToString:@"US"]);
- XCTAssertTrue([[metadata objectForKey:@"platformArchitecture"] isEqualToString:@"arm64"]);
- XCTAssertTrue([[metadata objectForKey:@"deviceType"] isEqualToString:@"iPhone9,1"]);
- XCTAssertTrue([threads objectForKey:@"crashed"]); // YES
- XCTAssertEqual([[threads objectForKey:@"registers"] count], 0); //{}
- XCTAssertEqual([[[threads objectForKey:@"stacktrace"] objectAtIndex:0] intValue], 74565);
- }
- #pragma mark - Path Helpers
- - (NSArray *)contentsOfActivePath {
- return [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.fileManager.activePath
- error:nil];
- }
- - (BOOL)metricKitFileExistsInCurrentReport:(BOOL)currentReport fatalReport:(BOOL)fatalReport {
- NSString *newestUnsentReportID =
- [self.existingReportManager.newestUnsentReport.reportID stringByAppendingString:@"/"];
- NSString *currentReportID =
- [_managerData.executionIDModel.executionID stringByAppendingString:@"/"];
- NSString *reportID = (currentReport ? currentReportID : newestUnsentReportID);
- NSString *metricKitName =
- fatalReport ? @"metric_kit_fatal.clsrecord" : @"metric_kit_nonfatal.clsrecord";
- NSString *temp =
- [[self.fileManager.activePath stringByAppendingString:@"/"] stringByAppendingString:reportID];
- // Need to determine which report the file should be in
- return [[NSFileManager defaultManager]
- fileExistsAtPath:[temp stringByAppendingString:metricKitName]];
- }
- - (NSString *)contentsOfMetricKitFile:(BOOL)currentReport fatalReport:(BOOL)fatalReport {
- if (![self metricKitFileExistsInCurrentReport:currentReport fatalReport:fatalReport]) return nil;
- NSString *newestUnsentReportID =
- [self.existingReportManager.newestUnsentReport.reportID stringByAppendingString:@"/"];
- NSString *currentReportID =
- [_managerData.executionIDModel.executionID stringByAppendingString:@"/"];
- NSString *reportID = (currentReport ? currentReportID : newestUnsentReportID);
- NSString *metricKitName =
- fatalReport ? @"metric_kit_fatal.clsrecord" : @"metric_kit_nonfatal.clsrecord";
- NSString *filePath = [[[self.fileManager.activePath stringByAppendingString:@"/"]
- stringByAppendingString:reportID] stringByAppendingString:metricKitName];
- NSString *fileContents = [[NSString alloc] initWithContentsOfFile:filePath
- encoding:NSUTF8StringEncoding
- error:nil];
- return fileContents;
- }
- - (NSDictionary *)contentsOfMetricKitFileAsDictionary:(BOOL)currentReport
- fatalReport:(BOOL)fatalReport {
- NSString *metricKitFileContents = [self contentsOfMetricKitFile:currentReport
- fatalReport:fatalReport];
- NSArray *metricKitFileArray = [metricKitFileContents componentsSeparatedByString:@"\n"];
- NSMutableDictionary *fileDictionary = [[NSMutableDictionary alloc] init];
- BOOL hasCrash = NO;
- for (NSString *json in metricKitFileArray) {
- NSString *itemKey = nil;
- if ([json containsString:@"metric_kit_fatal"])
- itemKey = @"crash_event";
- else if ([json containsString:@"exception"] && [json containsString:@"hang_event"])
- itemKey = @"hang_event";
- else if ([json containsString:@"exception"] && [json containsString:@"cpu_exception_event"])
- itemKey = @"cpu_exception_event";
- else if ([json containsString:@"exception"] &&
- [json containsString:@"disk_write_exception_event"])
- itemKey = @"disk_write_exception_event";
- else if ([json containsString:@"end_time"])
- itemKey = @"time";
- else if ([json containsString:@"threads"])
- itemKey = @"threads";
- NSData *itemData = [json dataUsingEncoding:NSUTF8StringEncoding];
- if (itemData == nil || itemKey == nil) continue;
- NSError *error = nil;
- NSDictionary *itemDictionary = [NSJSONSerialization JSONObjectWithData:itemData
- options:0
- error:&error];
- [fileDictionary setObject:itemDictionary forKey:itemKey];
- if ([itemKey isEqualToString:@"crash_event"]) {
- XCTAssertTrue(hasCrash == NO, "MetricKit reports should only have one crash event");
- hasCrash = YES;
- }
- }
- return fileDictionary;
- }
- - (void)createUnsentFatalReport {
- // create a report and put it in place
- NSString *reportPath =
- [self.fileManager.activePath stringByAppendingPathComponent:@"my_session_id"];
- FIRCLSInternalReport *report = [[FIRCLSInternalReport alloc] initWithPath:reportPath
- executionIdentifier:@"my_session_id"];
- [self.fileManager createDirectoryAtPath:report.path];
- [self.existingReportManager setShouldHaveExistingReport];
- }
- #pragma mark - Diagnostic Handling
- - (void)testEmptyDiagnosticHandling {
- FIRCLSMockMXDiagnosticPayload *emptyPayload = [self createEmptyDiagnosticPayload];
- [self.metricKitManager didReceiveDiagnosticPayloads:@[ emptyPayload ]];
- XCTAssertFalse([self metricKitFileExistsInCurrentReport:YES fatalReport:NO],
- "MetricKit report should not exist");
- }
- - (void)testCrashDiagnosticHandling {
- [self createUnsentFatalReport];
- FIRCLSMockMXDiagnosticPayload *crashPayload = [self createCrashDiagnosticPayload];
- [self.metricKitManager didReceiveDiagnosticPayloads:@[ crashPayload ]];
- XCTAssertTrue([self metricKitFileExistsInCurrentReport:NO fatalReport:YES],
- "MetricKit report should exist");
- NSDictionary *fileDictionary = [self contentsOfMetricKitFileAsDictionary:NO fatalReport:YES];
- XCTAssertNotNil(fileDictionary, "MetricKit file should not be empty");
- NSDictionary *crashDictionary =
- [[fileDictionary objectForKey:@"crash_event"] objectForKey:@"metric_kit_fatal"];
- XCTAssertNotNil(crashDictionary, "MetricKit event should include a crash diagnostic");
- XCTAssertEqual([[crashDictionary objectForKey:@"time"] longValue],
- [[NSNumber numberWithDouble:[self.beginTime timeIntervalSince1970]] longValue]);
- XCTAssertEqual([[crashDictionary objectForKey:@"end_time"] longValue],
- [[NSNumber numberWithDouble:[self.endTime timeIntervalSince1970]] longValue]);
- XCTAssertEqual([[crashDictionary objectForKey:@"signal"] integerValue], 11);
- XCTAssertTrue([[crashDictionary objectForKey:@"app_version"] isEqualToString:@"1"]);
- XCTAssertTrue([[crashDictionary objectForKey:@"termination_reason"]
- isEqualToString:@"Namespace SIGNAL, Code 0xb"]);
- XCTAssertTrue([[crashDictionary objectForKey:@"virtual_memory_region_info"]
- isEqualToString:
- @"0 is not in any region. Bytes before following region: 4000000000 REGION TYPE "
- @" START - END [ VSIZE] PRT\\/MAX SHRMOD REGION DETAIL UNUSED "
- @"SPACE AT START ---> __TEXT 0000000000000000-0000000000000000 [ 32K] "
- @"r-x\\/r-x SM=COW ...pp\\/Test"]);
- XCTAssertEqual([[crashDictionary objectForKey:@"exception_code"] integerValue], 0);
- XCTAssertEqual([[crashDictionary objectForKey:@"exception_type"] integerValue], 6);
- XCTAssertTrue([[crashDictionary objectForKey:@"name"] isEqualToString:@"EXC_BREAKPOINT"]);
- XCTAssertTrue([[crashDictionary objectForKey:@"code_name"] isEqualToString:@"EXC_I386_DIVERR"]);
- NSDictionary *metadata = [crashDictionary objectForKey:@"metadata"];
- NSDictionary *threads =
- [[[fileDictionary objectForKey:@"threads"] objectForKey:@"threads"] objectAtIndex:0];
- [self checkMetadata:metadata andThreads:threads];
- }
- - (void)testHangDiagnosticHandling {
- FIRCLSMockMXDiagnosticPayload *hangPayload = [self createHangDiagnosticPayload];
- [self.metricKitManager didReceiveDiagnosticPayloads:@[ hangPayload ]];
- XCTAssertTrue([self metricKitFileExistsInCurrentReport:YES fatalReport:NO],
- "MetricKit report should exist");
- NSDictionary *fileDictionary = [self contentsOfMetricKitFileAsDictionary:YES fatalReport:NO];
- XCTAssertNotNil(fileDictionary, "MetricKit file should not be empty");
- NSDictionary *hangDictionary =
- [[fileDictionary objectForKey:@"hang_event"] objectForKey:@"exception"];
- XCTAssertNotNil(hangDictionary, "MetricKit event should include a hang diagnostic");
- XCTAssertEqual([[hangDictionary objectForKey:@"hang_duration"] integerValue], 4);
- XCTAssertEqual([[hangDictionary objectForKey:@"time"] longValue],
- [[NSNumber numberWithDouble:[self.beginTime timeIntervalSince1970]] longValue]);
- XCTAssertEqual([[hangDictionary objectForKey:@"end_time"] longValue],
- [[NSNumber numberWithDouble:[self.endTime timeIntervalSince1970]] longValue]);
- XCTAssertTrue([[hangDictionary objectForKey:@"app_version"] isEqualToString:@"1"]);
- NSDictionary *metadata = [hangDictionary objectForKey:@"metadata"];
- NSDictionary *threads = [[hangDictionary objectForKey:@"threads"] objectAtIndex:0];
- [self checkMetadata:metadata andThreads:threads];
- }
- - (void)testCPUExceptionDiagnosticHandling {
- FIRCLSMockMXDiagnosticPayload *cpuPayload = [self createCPUExceptionDiagnosticPayload];
- [self.metricKitManager didReceiveDiagnosticPayloads:@[ cpuPayload ]];
- XCTAssertTrue([self metricKitFileExistsInCurrentReport:YES fatalReport:NO],
- "MetricKit report should exist");
- NSDictionary *fileDictionary = [self contentsOfMetricKitFileAsDictionary:YES fatalReport:NO];
- XCTAssertNotNil(fileDictionary, "MetricKit file should not be empty");
- NSDictionary *cpuDictionary =
- [[fileDictionary objectForKey:@"cpu_exception_event"] objectForKey:@"exception"];
- XCTAssertNotNil(cpuDictionary, "MetricKit event should include a CPU exception diagnostic");
- XCTAssertEqual([[cpuDictionary objectForKey:@"total_cpu_time"] integerValue], 1);
- XCTAssertEqual([[cpuDictionary objectForKey:@"total_sampled_time"] integerValue], 2);
- XCTAssertTrue([[cpuDictionary objectForKey:@"app_version"] isEqualToString:@"1"]);
- XCTAssertEqual([[cpuDictionary objectForKey:@"time"] longValue],
- [[NSNumber numberWithDouble:[self.beginTime timeIntervalSince1970]] longValue]);
- XCTAssertEqual([[cpuDictionary objectForKey:@"end_time"] longValue],
- [[NSNumber numberWithDouble:[self.endTime timeIntervalSince1970]] longValue]);
- NSDictionary *metadata = [cpuDictionary objectForKey:@"metadata"];
- NSDictionary *threads = [[cpuDictionary objectForKey:@"threads"] objectAtIndex:0];
- [self checkMetadata:metadata andThreads:threads];
- }
- - (void)testDiskWriteExceptionDiagnosticHandling {
- FIRCLSMockMXDiagnosticPayload *diskWritePayload =
- [self createDiskWriteExceptionDiagnosticPayload];
- [self.metricKitManager didReceiveDiagnosticPayloads:@[ diskWritePayload ]];
- XCTAssertTrue([self metricKitFileExistsInCurrentReport:YES fatalReport:NO],
- "MetricKit report should exist");
- NSDictionary *fileDictionary = [self contentsOfMetricKitFileAsDictionary:YES fatalReport:NO];
- XCTAssertNotNil(fileDictionary, "MetricKit file should not be empty");
- NSDictionary *diskWriteDictionary =
- [[fileDictionary objectForKey:@"disk_write_exception_event"] objectForKey:@"exception"];
- XCTAssertNotNil(diskWriteDictionary,
- "MetricKit event should include a disk write exception diagnostic");
- XCTAssertEqual([[diskWriteDictionary objectForKey:@"total_writes_caused"] longValue], 24);
- XCTAssertTrue([[diskWriteDictionary objectForKey:@"app_version"] isEqualToString:@"1"]);
- XCTAssertEqual([[diskWriteDictionary objectForKey:@"time"] longValue],
- [[NSNumber numberWithDouble:[self.beginTime timeIntervalSince1970]] longValue]);
- XCTAssertEqual([[diskWriteDictionary objectForKey:@"end_time"] longValue],
- [[NSNumber numberWithDouble:[self.endTime timeIntervalSince1970]] longValue]);
- NSDictionary *metadata = [diskWriteDictionary objectForKey:@"metadata"];
- NSDictionary *threads = [[diskWriteDictionary objectForKey:@"threads"] objectAtIndex:0];
- [self checkMetadata:metadata andThreads:threads];
- }
- - (void)testFullDiagnosticHandling {
- [self createUnsentFatalReport];
- FIRCLSMockMXDiagnosticPayload *fullPayload = [self createFullDiagnosticPayload];
- [self.metricKitManager didReceiveDiagnosticPayloads:@[ fullPayload ]];
- XCTAssertTrue([self metricKitFileExistsInCurrentReport:NO fatalReport:YES],
- "MetricKit fatal report should exist");
- XCTAssertTrue([self metricKitFileExistsInCurrentReport:YES fatalReport:NO],
- "MetricKit nonfatal report should exist");
- NSDictionary *fatalFileDictionary = [self contentsOfMetricKitFileAsDictionary:NO fatalReport:YES];
- NSDictionary *fileDictionary = [self contentsOfMetricKitFileAsDictionary:YES fatalReport:NO];
- XCTAssertNotNil(fileDictionary, "MetricKit nonfatal file should not be empty");
- XCTAssertNotNil(fatalFileDictionary, "MetricKit fatal file should not be empty");
- XCTAssertNil([fatalFileDictionary objectForKey:@"hang_event"]);
- XCTAssertNil([fatalFileDictionary objectForKey:@"cpu_exception_event"]);
- XCTAssertNil([fatalFileDictionary objectForKey:@"disk_write_exception_event"]);
- XCTAssertNil([fileDictionary objectForKey:@"crash_event"]);
- XCTAssertNil([fileDictionary objectForKey:@"time"]);
- NSDictionary *hangDictionary =
- [[fileDictionary objectForKey:@"hang_event"] objectForKey:@"exception"];
- NSDictionary *cpuDictionary =
- [[fileDictionary objectForKey:@"cpu_exception_event"] objectForKey:@"exception"];
- NSDictionary *diskDictionary =
- [[fileDictionary objectForKey:@"disk_write_exception_event"] objectForKey:@"exception"];
- NSDictionary *crashDictionary =
- [[fatalFileDictionary objectForKey:@"crash_event"] objectForKey:@"metric_kit_fatal"];
- XCTAssertNotNil(hangDictionary, "MetricKit event should include a hang diagnostic");
- XCTAssertNotNil(cpuDictionary, "MetricKit event should include a CPU exception diagnostic");
- XCTAssertNotNil(diskDictionary,
- "MetricKit event should include a disk write exception diagnostic");
- XCTAssertNotNil(crashDictionary, "MetricKit event should include a crash diagnostic");
- }
- - (void)testPayloadWithMultipleCrashesHandling {
- [self createUnsentFatalReport];
- FIRCLSMockMXDiagnosticPayload *payloadWithMultipleCrashes =
- [self createDiagnosticPayloadWithMultipleCrashes];
- [self.metricKitManager didReceiveDiagnosticPayloads:@[ payloadWithMultipleCrashes ]];
- XCTAssertTrue([self metricKitFileExistsInCurrentReport:NO fatalReport:YES],
- "MetricKit report should exist");
- NSDictionary *fileDictionary = [self contentsOfMetricKitFileAsDictionary:NO fatalReport:YES];
- XCTAssertNotNil(fileDictionary, "MetricKit file should not be empty");
- NSDictionary *crashDictionary =
- [[fileDictionary objectForKey:@"crash_event"] objectForKey:@"metric_kit_fatal"];
- XCTAssertNotNil(crashDictionary, "MetricKit event should include a crash diagnostic");
- }
- - (void)testMultiplePayloadsWithCrashesHandling {
- [self createUnsentFatalReport];
- FIRCLSMockMXDiagnosticPayload *crashPayload = [self createCrashDiagnosticPayload];
- FIRCLSMockMXDiagnosticPayload *hangPayload = [self createHangDiagnosticPayload];
- FIRCLSMockMXDiagnosticPayload *cpuPayload = [self createCPUExceptionDiagnosticPayload];
- [self.metricKitManager
- didReceiveDiagnosticPayloads:@[ crashPayload, hangPayload, crashPayload, cpuPayload ]];
- XCTAssertTrue([self metricKitFileExistsInCurrentReport:NO fatalReport:YES],
- "MetricKit fatal report should exist");
- XCTAssertTrue([self metricKitFileExistsInCurrentReport:YES fatalReport:NO],
- "MetricKit nonfatal report should exist");
- NSDictionary *fatalFileDictionary = [self contentsOfMetricKitFileAsDictionary:NO fatalReport:YES];
- NSDictionary *fileDictionary = [self contentsOfMetricKitFileAsDictionary:YES fatalReport:NO];
- XCTAssertNotNil(fileDictionary, "MetricKit nonfatal file should not be empty");
- XCTAssertNotNil(fatalFileDictionary, "MetricKit fatal file should not be empty");
- XCTAssertNil([fatalFileDictionary objectForKey:@"hang_event"]);
- XCTAssertNil([fatalFileDictionary objectForKey:@"cpu_exception_event"]);
- XCTAssertNil([fatalFileDictionary objectForKey:@"disk_write_exception_event"]);
- XCTAssertNil([fileDictionary objectForKey:@"crash_event"]);
- XCTAssertNil([fileDictionary objectForKey:@"time"]);
- NSDictionary *hangDictionary =
- [[fileDictionary objectForKey:@"hang_event"] objectForKey:@"exception"];
- NSDictionary *cpuDictionary =
- [[fileDictionary objectForKey:@"cpu_exception_event"] objectForKey:@"exception"];
- NSDictionary *crashDictionary =
- [[fatalFileDictionary objectForKey:@"crash_event"] objectForKey:@"metric_kit_fatal"];
- XCTAssertNotNil(hangDictionary, "MetricKit event should include a hang diagnostic");
- XCTAssertNotNil(cpuDictionary, "MetricKit event should include a CPU exception diagnostic");
- XCTAssertNotNil(crashDictionary, "MetricKit event should include a crash diagnostic");
- }
- @end
- #endif
|