Merge branch 'refs/heads/feature/CustomAlert' into feature/CrashReporting

This commit is contained in:
Andreas Linde 2014-04-27 19:45:43 +02:00
commit 8d8289d970
4 changed files with 227 additions and 63 deletions

View File

@ -39,6 +39,11 @@
#import <CrashReporter/CrashReporter.h> #import <CrashReporter/CrashReporter.h>
#endif #endif
/**
* Custom block that handles the alert that prompts the user whether he wants to send crash reports
*/
typedef void(^CustomAlertViewHandler)();
/** /**
* Crash Manager status * Crash Manager status
@ -59,6 +64,26 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) {
}; };
/**
* Crash Manager alert user input
*/
typedef NS_ENUM(NSUInteger, BITCrashManagerUserInput) {
/**
* User chose not to send the crash report
*/
BITCrashManagerUserInputDontSend = 0,
/**
* User wants the crash report to be sent
*/
BITCrashManagerUserInputSend = 1,
/**
* User chose to always send crash reports
*/
BITCrashManagerUserInputAlwaysSend = 2
};
@protocol BITCrashManagerDelegate; @protocol BITCrashManagerDelegate;
/** /**
@ -202,7 +227,7 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) {
* *
* @param callbacks A pointer to an initialized PLCrashReporterCallback structure, see https://www.plcrashreporter.org/documentation/api/v1.2-rc2/struct_p_l_crash_reporter_callbacks.html * @param callbacks A pointer to an initialized PLCrashReporterCallback structure, see https://www.plcrashreporter.org/documentation/api/v1.2-rc2/struct_p_l_crash_reporter_callbacks.html
*/ */
- (void)setCrashCallbacks: (PLCrashReporterCallbacks *) callbacks; - (void)setCrashCallbacks:(PLCrashReporterCallbacks *)callbacks;
/** /**
@ -237,6 +262,26 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) {
*/ */
@property (nonatomic, readonly) BOOL didCrashInLastSession; @property (nonatomic, readonly) BOOL didCrashInLastSession;
/**
Provides an interface to handle user input from a custom alert
@param userInput On this input depends, whether crash reports are sent, always sent or not sent and deleted.
@param userProvidedCrashDescription The content of this optional string will be attached to the crash report as the description and allows to ask the user for e.g. additional comments or info
@return Returns YES if the input is a valid option and successfully triggered further processing of the crash report
@see BITCrashManagerUserInput
*/
- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString*)userProvidedCrashDescription;
/**
Lets you set a custom block which handles showing a custom UI and asking the user
whether he wants to send the crash report.
@param alertViewHandler A block that is responsible for loading, presenting and and dismissing your custom user interface which prompts the user if he wants to send crash reports. The block is also responsible for triggering further processing of the crash reports.
@warning Block needs to call the `handleUserInput:withUserProvidedCrashDescription` method!
*/
- (void) setAlertViewHandler:(CustomAlertViewHandler)alertViewHandler;
/** /**
Provides the time between startup and crash in seconds Provides the time between startup and crash in seconds

View File

@ -65,7 +65,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
@interface BITCrashManager () @interface BITCrashManager ()
@property (nonatomic, strong) NSFileManager *fileManager; @property (nonatomic, copy, setter = setAlertViewHandler:) CustomAlertViewHandler alertViewHandler;
@end @end
@ -74,6 +74,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
NSMutableArray *_crashFiles; NSMutableArray *_crashFiles;
NSString *_crashesDir; NSString *_crashesDir;
NSString *_lastCrashFilename;
NSString *_settingsFile; NSString *_settingsFile;
NSString *_analyzerInProgressFile; NSString *_analyzerInProgressFile;
NSFileManager *_fileManager; NSFileManager *_fileManager;
@ -114,6 +115,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
_timeintervalCrashInLastSessionOccured = -1; _timeintervalCrashInLastSessionOccured = -1;
_approvedCrashReports = [[NSMutableDictionary alloc] init]; _approvedCrashReports = [[NSMutableDictionary alloc] init];
_alertViewHandler = nil;
_fileManager = [[NSFileManager alloc] init]; _fileManager = [[NSFileManager alloc] init];
_crashFiles = [[NSMutableArray alloc] init]; _crashFiles = [[NSMutableArray alloc] init];
@ -174,9 +176,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
NSString *errorString = nil; NSString *errorString = nil;
NSMutableDictionary *rootObj = [NSMutableDictionary dictionaryWithCapacity:2]; NSMutableDictionary *rootObj = [NSMutableDictionary dictionaryWithCapacity:2];
if (_approvedCrashReports && [_approvedCrashReports count] > 0) if (_approvedCrashReports && [_approvedCrashReports count] > 0) {
[rootObj setObject:_approvedCrashReports forKey:kBITCrashApprovedReports]; [rootObj setObject:_approvedCrashReports forKey:kBITCrashApprovedReports];
}
NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)rootObj NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)rootObj
format:NSPropertyListBinaryFormat_v1_0 format:NSPropertyListBinaryFormat_v1_0
errorDescription:&errorString]; errorDescription:&errorString];
@ -221,14 +223,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
NSError *error = NULL; NSError *error = NULL;
for (NSUInteger i=0; i < [_crashFiles count]; i++) { for (NSUInteger i=0; i < [_crashFiles count]; i++) {
[_fileManager removeItemAtPath:[_crashFiles objectAtIndex:i] error:&error]; NSString *filename = [_crashFiles objectAtIndex:i];
[_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".data"] error:&error];
[_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".meta"] error:&error];
NSString *cacheFilename = [[_crashFiles objectAtIndex:i] lastPathComponent]; NSString *cacheFilename = [[_crashFiles objectAtIndex:i] lastPathComponent];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; [self cleanFilesAndKeychainWithFileName:filename CacheFilename:cacheFilename Error:error];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]];
} }
[_crashFiles removeAllObjects]; [_crashFiles removeAllObjects];
[_approvedCrashReports removeAllObjects]; [_approvedCrashReports removeAllObjects];
@ -236,6 +235,16 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
[self saveSettings]; [self saveSettings];
} }
- (void)cleanFilesAndKeychainWithFileName:(NSString *)filename CacheFilename:(NSString *)cacheFilename Error:(NSError *)error{
[_fileManager removeItemAtPath:filename error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.desc", filename] error:&error];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]];
}
- (void)persistAttachment:(BITCrashAttachment *)attachment withFilename:(NSString *)filename { - (void)persistAttachment:(BITCrashAttachment *)attachment withFilename:(NSString *)filename {
NSString *attachmentFilename = [filename stringByAppendingString:@".data"]; NSString *attachmentFilename = [filename stringByAppendingString:@".data"];
@ -249,6 +258,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
[data writeToFile:attachmentFilename atomically:YES]; [data writeToFile:attachmentFilename atomically:YES];
} }
- (void)persistUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription {
if (userProvidedCrashDescription && [userProvidedCrashDescription length] > 0) {
NSError *error;
[userProvidedCrashDescription writeToFile:[NSString stringWithFormat:@"%@.desc", [_crashesDir stringByAppendingPathComponent: _lastCrashFilename]] atomically:YES encoding:NSUTF8StringEncoding error:&error];
}
}
/** /**
* Read the attachment data from the stored file * Read the attachment data from the stored file
* *
@ -434,14 +450,22 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
return useremail; return useremail;
} }
- (NSString *)getCrashesDir {
return _crashesDir;
}
#pragma mark - Public #pragma mark - Public
- (void)setCrashCallbacks: (PLCrashReporterCallbacks *) callbacks { - (void)setCrashCallbacks:(PLCrashReporterCallbacks *)callbacks {
_crashCallBacks = callbacks; _crashCallBacks = callbacks;
} }
- (void)setAlertViewHandler:(CustomAlertViewHandler)alertViewHandler{
_alertViewHandler = alertViewHandler;
}
/** /**
* Check if the debugger is attached * Check if the debugger is attached
* *
@ -488,6 +512,41 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
} }
- (BOOL)handleUserInput:(BITCrashManagerUserInput)userInput withUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription{
switch (userInput) {
case BITCrashManagerUserInputDontSend:
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) {
[self.delegate crashManagerWillCancelSendingCrashReport:self];
}
[self cleanCrashReports];
return YES;
case BITCrashManagerUserInputSend:
[self persistUserProvidedCrashDescription:userProvidedCrashDescription];
[self sendCrashReports];
return YES;
case BITCrashManagerUserInputAlwaysSend:
_crashManagerStatus = BITCrashManagerStatusAutoSend;
[[NSUserDefaults standardUserDefaults] setInteger:_crashManagerStatus forKey:kBITCrashManagerStatus];
[[NSUserDefaults standardUserDefaults] synchronize];
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillSendCrashReportsAlways:)]) {
[self.delegate crashManagerWillSendCrashReportsAlways:self];
}
[self persistUserProvidedCrashDescription:userProvidedCrashDescription];
[self sendCrashReports];
return YES;
default:
return NO;
}
}
#pragma mark - PLCrashReporter #pragma mark - PLCrashReporter
/** /**
@ -513,6 +572,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
NSData *crashData = [[NSData alloc] initWithData:[self.plCrashReporter loadPendingCrashReportDataAndReturnError: &error]]; NSData *crashData = [[NSData alloc] initWithData:[self.plCrashReporter loadPendingCrashReportDataAndReturnError: &error]];
NSString *cacheFilename = [NSString stringWithFormat: @"%.0f", [NSDate timeIntervalSinceReferenceDate]]; NSString *cacheFilename = [NSString stringWithFormat: @"%.0f", [NSDate timeIntervalSinceReferenceDate]];
_lastCrashFilename = cacheFilename;
if (crashData == nil) { if (crashData == nil) {
BITHockeyLog(@"ERROR: Could not load crash report: %@", error); BITHockeyLog(@"ERROR: Could not load crash report: %@", error);
@ -596,7 +656,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
/** /**
* Check if there are any new crash reports that are not yet processed * Check if there are any new crash reports that are not yet processed
* *
* @return `YES` if ther eis at least one new crash report found, `NO` otherwise * @return `YES` if there is at least one new crash report found, `NO` otherwise
*/ */
- (BOOL)hasPendingCrashReport { - (BOOL)hasPendingCrashReport {
if (_crashManagerStatus == BITCrashManagerStatusDisabled) return NO; if (_crashManagerStatus == BITCrashManagerStatusDisabled) return NO;
@ -614,7 +674,8 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
![file hasSuffix:@".analyzer"] && ![file hasSuffix:@".analyzer"] &&
![file hasSuffix:@".plist"] && ![file hasSuffix:@".plist"] &&
![file hasSuffix:@".data"] && ![file hasSuffix:@".data"] &&
![file hasSuffix:@".meta"]) { ![file hasSuffix:@".meta"] &&
![file hasSuffix:@".desc"]) {
[_crashFiles addObject:[_crashesDir stringByAppendingPathComponent: file]]; [_crashFiles addObject:[_crashesDir stringByAppendingPathComponent: file]];
} }
} }
@ -692,17 +753,21 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
alertDescription = [NSString stringWithFormat:BITHockeyLocalizedString(@"CrashDataFoundDescription"), appName]; alertDescription = [NSString stringWithFormat:BITHockeyLocalizedString(@"CrashDataFoundDescription"), appName];
} }
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:BITHockeyLocalizedString(@"CrashDataFoundTitle"), appName] if (_alertViewHandler) {
message:alertDescription _alertViewHandler();
delegate:self } else {
cancelButtonTitle:BITHockeyLocalizedString(@"CrashDontSendReport") UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:BITHockeyLocalizedString(@"CrashDataFoundTitle"), appName]
otherButtonTitles:BITHockeyLocalizedString(@"CrashSendReport"), nil]; message:alertDescription
delegate:self
cancelButtonTitle:BITHockeyLocalizedString(@"CrashDontSendReport")
otherButtonTitles:BITHockeyLocalizedString(@"CrashSendReport"), nil];
if (self.shouldShowAlwaysButton) { if (self.shouldShowAlwaysButton) {
[alertView addButtonWithTitle:BITHockeyLocalizedString(@"CrashSendReportAlways")]; [alertView addButtonWithTitle:BITHockeyLocalizedString(@"CrashSendReportAlways")];
}
[alertView show];
} }
[alertView show];
} else { } else {
[self sendCrashReports]; [self sendCrashReports];
} }
@ -826,13 +891,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
if (report == nil) { if (report == nil) {
BITHockeyLog(@"WARNING: Could not parse crash report"); BITHockeyLog(@"WARNING: Could not parse crash report");
// we cannot do anything with this report, so delete it // we cannot do anything with this report, so delete it
[_fileManager removeItemAtPath:filename error:&error]; [self cleanFilesAndKeychainWithFileName:filename CacheFilename:cacheFilename Error:error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]];
continue; continue;
} }
@ -872,6 +931,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]] ?: @""; useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]] ?: @"";
userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]] ?: @""; userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]] ?: @"";
applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @""; applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @"";
description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@.desc", [_crashesDir stringByAppendingPathComponent: cacheFilename]] encoding:NSUTF8StringEncoding error:&error];
BITCrashAttachment *attachment = [self attachmentForCrashReport:filename]; BITCrashAttachment *attachment = [self attachmentForCrashReport:filename];
if (attachment) { if (attachment) {
@ -884,7 +944,11 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
} }
if ([applicationLog length] > 0) { if ([applicationLog length] > 0) {
description = [NSString stringWithFormat:@"%@", applicationLog]; if ([description length] > 0) {
description = [NSString stringWithFormat:@"%@\n\nLog:\n%@", description, applicationLog];
} else {
description = [NSString stringWithFormat:@"Log:\n%@", applicationLog];
}
} }
[crashes appendFormat:@"<crash><applicationname>%s</applicationname><uuids>%@</uuids><bundleidentifier>%@</bundleidentifier><systemversion>%@</systemversion><platform>%@</platform><senderversion>%@</senderversion><version>%@</version><uuid>%@</uuid><log><![CDATA[%@]]></log><userid>%@</userid><username>%@</username><contact>%@</contact><installstring>%@</installstring><description><![CDATA[%@]]></description></crash>", [crashes appendFormat:@"<crash><applicationname>%s</applicationname><uuids>%@</uuids><bundleidentifier>%@</bundleidentifier><systemversion>%@</systemversion><platform>%@</platform><senderversion>%@</senderversion><version>%@</version><uuid>%@</uuid><log><![CDATA[%@]]></log><userid>%@</userid><username>%@</username><contact>%@</contact><installstring>%@</installstring><description><![CDATA[%@]]></description></crash>",
@ -908,13 +972,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
[_approvedCrashReports setObject:[NSNumber numberWithBool:YES] forKey:filename]; [_approvedCrashReports setObject:[NSNumber numberWithBool:YES] forKey:filename];
} else { } else {
// we cannot do anything with this report, so delete it // we cannot do anything with this report, so delete it
[_fileManager removeItemAtPath:filename error:&error]; [self cleanFilesAndKeychainWithFileName:filename CacheFilename:cacheFilename Error:error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]];
} }
} }
@ -932,35 +990,20 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
switch (buttonIndex) { switch (buttonIndex) {
case 0: case 0:
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillCancelSendingCrashReport:)]) { [self handleUserInput:BITCrashManagerUserInputDontSend withUserProvidedCrashDescription:nil];
[self.delegate crashManagerWillCancelSendingCrashReport:self];
}
_sendingInProgress = NO;
[self cleanCrashReports];
break; break;
case 1: case 1:
[self sendCrashReports]; [self handleUserInput:BITCrashManagerUserInputSend withUserProvidedCrashDescription:nil];
break; break;
case 2: { case 2:
_crashManagerStatus = BITCrashManagerStatusAutoSend; [self handleUserInput:BITCrashManagerUserInputAlwaysSend withUserProvidedCrashDescription:nil];
[[NSUserDefaults standardUserDefaults] setInteger:_crashManagerStatus forKey:kBITCrashManagerStatus];
[[NSUserDefaults standardUserDefaults] synchronize];
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashManagerWillSendCrashReportsAlways:)]) {
[self.delegate crashManagerWillSendCrashReportsAlways:self];
}
[self sendCrashReports];
break;
}
default:
_sendingInProgress = NO;
[self cleanCrashReports];
break; break;
} }
} }
#pragma mark - Networking #pragma mark - Networking
/** /**

View File

@ -36,8 +36,12 @@
@property (nonatomic) NSUncaughtExceptionHandler *exceptionHandler; @property (nonatomic) NSUncaughtExceptionHandler *exceptionHandler;
@property (nonatomic, strong) NSFileManager *fileManager;
@property (nonatomic, strong) BITPLCrashReporter *plCrashReporter; @property (nonatomic, strong) BITPLCrashReporter *plCrashReporter;
@property (nonatomic) NSString *lastCrashFilename;
#if HOCKEYSDK_FEATURE_AUTHENTICATOR #if HOCKEYSDK_FEATURE_AUTHENTICATOR
// Only set via BITAuthenticator // Only set via BITAuthenticator
@ -61,9 +65,17 @@
- (BOOL)hasPendingCrashReport; - (BOOL)hasPendingCrashReport;
- (BOOL)hasNonApprovedCrashReports; - (BOOL)hasNonApprovedCrashReports;
- (void)persistUserProvidedCrashDescription:(NSString *)userProvidedCrashDescription;
- (void)persistAttachment:(BITCrashAttachment *)attachment withFilename:(NSString *)filename;
- (BITCrashAttachment *)attachmentForCrashReport:(NSString *)filename;
- (void)invokeDelayedProcessing; - (void)invokeDelayedProcessing;
- (void)sendCrashReports; - (void)sendCrashReports;
- (NSString *)getCrashesDir;
- (void)setLastCrashFilename:(NSString *)lastCrashFilename;
@end @end

View File

@ -23,6 +23,7 @@
#import "BITTestHelper.h" #import "BITTestHelper.h"
#define kBITCrashMetaAttachment @"BITCrashMetaAttachment"
@interface BITCrashManagerTests : SenTestCase @interface BITCrashManagerTests : SenTestCase
@ -71,7 +72,6 @@
[self startManager]; [self startManager];
} }
#pragma mark - Setup Tests #pragma mark - Setup Tests
- (void)testThatItInstantiates { - (void)testThatItInstantiates {
@ -81,6 +81,32 @@
#pragma mark - Persistence tests #pragma mark - Persistence tests
- (void)testPersistUserProvidedCrashDescription {
NSString *tempCrashName = @"tempCrash";
[_sut setLastCrashFilename:tempCrashName];
[_sut persistUserProvidedCrashDescription:@"Test string"];
NSError *error;
NSString *description = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@.desc", [[_sut getCrashesDir] stringByAppendingPathComponent: tempCrashName]] encoding:NSUTF8StringEncoding error:&error];
assertThat(description, equalTo(@"Test string"));
}
- (void)testPersistAttachment {
NSString *filename = @"TestAttachment";
NSData *data = [[NSData alloc] initWithBase64Encoding:@"TestData"];
NSString* type = @"text/plain";
BITCrashAttachment *originalAttachment = [[BITCrashAttachment alloc] initWithFilename:filename attachmentData:data contentType:type];
NSString *attachmentFilename = [_sut.getCrashesDir stringByAppendingPathComponent:@"testAttachment"];
[_sut persistAttachment:originalAttachment withFilename:attachmentFilename];
BITCrashAttachment *decodedAttachment = [_sut attachmentForCrashReport:attachmentFilename];
assertThat(decodedAttachment.filename, equalTo(filename));
assertThat(decodedAttachment.attachmentData, equalTo(data));
assertThat(decodedAttachment.contentType, equalTo(type));
}
#pragma mark - Helper #pragma mark - Helper
@ -123,6 +149,44 @@
[verifyCount(delegateMock, times(1)) userEmailForHockeyManager:hm componentManager:_sut]; [verifyCount(delegateMock, times(1)) userEmailForHockeyManager:hm componentManager:_sut];
} }
#pragma mark - Handle User Input
- (void)testHandleUserInputDontSend {
id <BITCrashManagerDelegate> delegateMock = mockProtocol(@protocol(BITCrashManagerDelegate));
_sut.delegate = delegateMock;
assertThatBool([_sut handleUserInput:BITCrashManagerUserInputDontSend withUserProvidedCrashDescription:nil], equalToBool(YES));
[verify(delegateMock) crashManagerWillCancelSendingCrashReport:_sut];
}
- (void)testHandleUserInputSend {
assertThatBool([_sut handleUserInput:BITCrashManagerUserInputSend withUserProvidedCrashDescription:nil], equalToBool(YES));
}
- (void)testHandleUserInputAlwaysSend {
id <BITCrashManagerDelegate> delegateMock = mockProtocol(@protocol(BITCrashManagerDelegate));
_sut.delegate = delegateMock;
NSUserDefaults *mockUserDefaults = mock([NSUserDefaults class]);
//Test if CrashManagerStatus is unset
[given([mockUserDefaults integerForKey:@"BITCrashManagerStatus"]) willReturn:nil];
//Test if method runs through
assertThatBool([_sut handleUserInput:BITCrashManagerUserInputAlwaysSend withUserProvidedCrashDescription:nil], equalToBool(YES));
//Test if correct CrashManagerStatus is now set
[given([mockUserDefaults integerForKey:@"BITCrashManagerStauts"]) willReturnInt:BITCrashManagerStatusAutoSend];
//Verify that delegate method has been called
[verify(delegateMock) crashManagerWillSendCrashReportsAlways:_sut];
}
- (void)testHandleUserInputWithInvalidInput {
assertThatBool([_sut handleUserInput:3 withUserProvidedCrashDescription:nil], equalToBool(NO));
}
#pragma mark - Debugger #pragma mark - Debugger