Add support for binary attachments to crash reports

This commit is contained in:
Andreas Linde 2014-02-27 18:21:17 +01:00
parent ce9014f111
commit 1068bb119c
9 changed files with 299 additions and 13 deletions

View File

@ -0,0 +1,66 @@
/*
* Author: Andreas Linde <mail@andreaslinde.de>
*
* Copyright (c) 2014 HockeyApp, Bit Stadium GmbH.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
/**
* Provides support to add binary attachments to crash reports
*
* This is used by `[BITCrashManagerDelegate attachmentForCrashManager:]`
*/
@interface BITCrashAttachment : NSObject<NSCoding>
/**
* The filename the attachment should get
*/
@property (nonatomic, readonly, strong) NSString *filename;
/**
* The attachment data as NSData object
*/
@property (nonatomic, readonly, strong) NSData *attachmentData;
/**
* The content type of your data as MIME type
*/
@property (nonatomic, readonly, strong) NSString *contentType;
/**
* Create an BITCrashAttachment instance with a given filename and NSData object
*
* @param filename The filename the attachment should get
* @param attachmentData The attachment data as NSData
* @param contentType The content type of your data as MIME type
*
* @return An instsance of BITCrashAttachment
*/
- (instancetype)initWithFilename:(NSString *)filename
attachmentData:(NSData *)attachmentData
contentType:(NSString *)contentType;
@end

View File

@ -0,0 +1,64 @@
/*
* Author: Andreas Linde <mail@andreaslinde.de>
*
* Copyright (c) 2014 HockeyApp, Bit Stadium GmbH.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#import "BITCrashAttachment.h"
@implementation BITCrashAttachment
- (instancetype)initWithFilename:(NSString *)filename
attachmentData:(NSData *)attachmentData
contentType:(NSString *)contentType
{
if (self = [super init]) {
_filename = filename;
_attachmentData = attachmentData;
_contentType = contentType;
}
return self;
}
#pragma mark - NSCoder
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.filename forKey:@"filename"];
[encoder encodeObject:self.attachmentData forKey:@"data"];
[encoder encodeObject:self.contentType forKey:@"contentType"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super init])) {
_filename = [decoder decodeObjectForKey:@"filename"];
_attachmentData = [decoder decodeObjectForKey:@"data"];
_contentType = [decoder decodeObjectForKey:@"contentType"];
}
return self;
}
@end

View File

@ -37,7 +37,9 @@
#import "HockeySDKPrivate.h"
#import "BITHockeyHelper.h"
#import "BITHockeyAppClient.h"
#import "BITCrashAttachment.h"
#import "BITHockeyBaseManagerPrivate.h"
#import "BITCrashManagerPrivate.h"
#import "BITCrashReportTextFormatter.h"
@ -52,6 +54,11 @@
#define kBITCrashMetaUserEmail @"BITCrashMetaUserEmail"
#define kBITCrashMetaUserID @"BITCrashMetaUserID"
#define kBITCrashMetaApplicationLog @"BITCrashMetaApplicationLog"
#define kBITCrashMetaAttachment @"BITCrashMetaAttachment"
// internal keys
NSString *const KBITAttachmentDictIndex = @"index";
NSString *const KBITAttachmentDictAttachment = @"attachment";
NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
@ -215,6 +222,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
for (NSUInteger i=0; i < [_crashFiles count]; i++) {
[_fileManager removeItemAtPath:[_crashFiles objectAtIndex:i] error:&error];
[_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".data"] error:&error];
[_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".meta"] error:&error];
NSString *cacheFilename = [[_crashFiles objectAtIndex:i] lastPathComponent];
@ -228,9 +236,57 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
[self saveSettings];
}
- (void)persistAttachment:(BITCrashAttachment *)attachment withFilename:(NSString *)filename {
NSString *attachmentFilename = [filename stringByAppendingString:@".data"];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:attachment forKey:kBITCrashMetaAttachment];
[archiver finishEncoding];
[data writeToFile:attachmentFilename atomically:YES];
}
/**
* Read the attachment data from the stored file
*
* @param filename The crash report id
*
* @return an BITCrashAttachment instance or nil
*/
- (BITCrashAttachment *)attachmentForCrashReport:(NSString *)filename {
NSString *attachmentFilename = [filename stringByAppendingString:@".data"];
if (![_fileManager fileExistsAtPath:attachmentFilename])
return nil;
NSData *codedData = [[NSData alloc] initWithContentsOfFile:attachmentFilename];
if (!codedData)
return nil;
NSKeyedUnarchiver *unarchiver = nil;
@try {
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:codedData];
}
@catch (NSException *exception) {
return nil;
}
if ([unarchiver containsValueForKey:kBITCrashMetaAttachment]) {
BITCrashAttachment *attachment = [unarchiver decodeObjectForKey:kBITCrashMetaAttachment];
return attachment;
}
return nil;
}
/**
* Extract all app sepcific UUIDs from the crash reports
*
*
* This allows us to send the UUIDs in the XML construct to the server, so the server does not need to parse the crash report for this data.
* The app specific UUIDs help to identify which dSYMs are needed to symbolicate this crash report.
*
@ -489,6 +545,14 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
}
[metaDict setObject:applicationLog forKey:kBITCrashMetaApplicationLog];
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(attachmentForCrashManager:)]) {
BITCrashAttachment *attachment = [self.delegate attachmentForCrashManager:self];
if (attachment) {
[self persistAttachment:attachment withFilename:[_crashesDir stringByAppendingPathComponent: cacheFilename]];
}
}
NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)metaDict
format:NSPropertyListBinaryFormat_v1_0
errorDescription:&errorString];
@ -549,6 +613,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
![file hasSuffix:@".DS_Store"] &&
![file hasSuffix:@".analyzer"] &&
![file hasSuffix:@".plist"] &&
![file hasSuffix:@".data"] &&
![file hasSuffix:@".meta"]) {
[_crashFiles addObject:[_crashesDir stringByAppendingPathComponent: file]];
}
@ -747,6 +812,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
NSError *error = NULL;
NSMutableString *crashes = nil;
NSMutableArray *attachments = [NSMutableArray array];
_crashIdenticalCurrentVersion = NO;
for (NSUInteger i=0; i < [_crashFiles count]; i++) {
@ -761,6 +827,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
BITHockeyLog(@"WARNING: Could not parse crash report");
// we cannot do anything with this report, so delete it
[_fileManager removeItemAtPath:filename error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]];
@ -805,6 +872,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]] ?: @"";
userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]] ?: @"";
applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @"";
BITCrashAttachment *attachment = [self attachmentForCrashReport:filename];
if (attachment) {
NSDictionary *attachmentDict = @{KBITAttachmentDictIndex: @(i),
KBITAttachmentDictAttachment: attachment};
[attachments addObject:attachmentDict];
}
} else {
BITHockeyLog(@"ERROR: Reading crash meta data. %@", error);
}
@ -835,6 +909,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
} else {
// we cannot do anything with this report, so delete it
[_fileManager removeItemAtPath:filename error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.data", filename] error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]];
@ -847,7 +922,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
if (crashes != nil) {
BITHockeyLog(@"INFO: Sending crash reports:\n%@", crashes);
[self postXML:[NSString stringWithFormat:@"<crashes>%@</crashes>", crashes]];
[self postXML:[NSString stringWithFormat:@"<crashes>%@</crashes>", crashes] attachments:attachments];
}
}
@ -895,7 +970,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
*
* @param xml The XML data that needs to be send to the server
*/
- (void)postXML:(NSString*)xml {
- (void)postXML:(NSString*)xml attachments:(NSArray *)attachments {
NSMutableURLRequest *request = nil;
NSString *boundary = @"----FOO";
@ -917,12 +992,28 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
[request setValue:contentType forHTTPHeaderField:@"Content-type"];
NSMutableData *postBody = [NSMutableData data];
[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[@"Content-Disposition: form-data; name=\"xml\"; filename=\"crash.xml\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Type: text/xml\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[xml dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[BITHockeyAppClient dataWithPostValue:[xml dataUsingEncoding:NSUTF8StringEncoding]
forKey:@"xml"
contentType:@"text/xml"
boundary:boundary
filename:@"crash.xml"]];
for (NSDictionary *dict in attachments) {
NSInteger index = [(NSNumber *)dict[KBITAttachmentDictIndex] integerValue];
NSString *key = [NSString stringWithFormat:@"attachment%ld", (long)index];
BITCrashAttachment *attachment = (BITCrashAttachment *)dict[KBITAttachmentDictAttachment];
[postBody appendData:[BITHockeyAppClient dataWithPostValue:attachment.attachmentData
forKey:key
contentType:attachment.contentType
boundary:boundary
filename:attachment.filename]];
}
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:postBody];
_statusCode = 200;

View File

@ -29,6 +29,7 @@
#import <Foundation/Foundation.h>
@class BITCrashManager;
@class BITCrashAttachment;
/**
The `BITCrashManagerDelegate` formal protocol defines methods further configuring
@ -47,17 +48,43 @@
/** Return any log string based data the crash report being processed should contain
@param crashManager The `BITCrashManager` instance invoking this delegate
@see attachmentForCrashManager:
@see userNameForCrashManager:
@see userEmailForCrashManager:
*/
-(NSString *)applicationLogForCrashManager:(BITCrashManager *)crashManager;
/** Return a BITCrashAttachment object providing an NSData object the crash report
being processed should contain
Please limit your attachments to reasonable files to avoid high traffic costs for your users.
Example implementation:
- (BITCrashAttachment *)attachmentForCrashManager:(BITCrashManager *)crashManager {
NSData *data = [NSData dataWithContentsOfURL:@"mydatafile"];
BITCrashAttachment *attachment = [[BITCrashAttachment alloc] initWithFilename:@"myfile.data"
attachmentData:data
contentType:@"'application/octet-stream"];
return attachment;
}
@param crashManager The `BITCrashManager` instance invoking this delegate
@see applicationLogForCrashManager:
@see userNameForCrashManager:
@see userEmailForCrashManager:
*/
-(BITCrashAttachment *)attachmentForCrashManager:(BITCrashManager *)crashManager;
/** Return the user name or userid that should be send along each crash report
@param crashManager The `BITCrashManager` instance invoking this delegate
@see applicationLogForCrashManager:
@see attachmentForCrashManager:
@see userEmailForCrashManager:
@deprecated Please use `BITHockeyManagerDelegate userNameForHockeyManager:componentManager:` instead
@warning When returning a non nil value, crash reports are not anonymous any
@ -71,6 +98,7 @@
@param crashManager The `BITCrashManager` instance invoking this delegate
@see applicationLogForCrashManager:
@see attachmentForCrashManager:
@see userNameForCrashManager:
@deprecated Please use `BITHockeyManagerDelegate userEmailForHockeyManager:componentManager:` instead
@warning When returning a non nil value, crash reports are not anonymous any

View File

@ -118,8 +118,8 @@
#pragma mark - Helpers
/**
* create a post body from the given value, key and boundary
* c/p from HockeyBaseManager
* create a post body from the given value, key and boundary. This is a convenience call to
* dataWithPostValue:forKey:contentType:boundary and aimed at NSString-content.
*
* @param value -
* @param key -
@ -128,4 +128,18 @@
* @return NSData instance configured to be attached on a (post) URLRequest
*/
+ (NSData *)dataWithPostValue:(NSString *)value forKey:(NSString *)key boundary:(NSString *) boundary;
/**
* create a post body from the given value, key and boundary and content type.
*
* @param value -
* @param key -
*@param contentType -
* @param boundary -
* @param filename -
*
* @return NSData instance configured to be attached on a (post) URLRequest
*/
+ (NSData *)dataWithPostValue:(NSData *)value forKey:(NSString *)key contentType:(NSString *)contentType boundary:(NSString *) boundary filename:(NSString *)filename;
@end

View File

@ -85,12 +85,25 @@
}
+ (NSData *)dataWithPostValue:(NSString *)value forKey:(NSString *)key boundary:(NSString *) boundary {
return [self dataWithPostValue:[value dataUsingEncoding:NSUTF8StringEncoding] forKey:key contentType:@"text" boundary:boundary filename:nil];
}
+ (NSData *)dataWithPostValue:(NSData *)value forKey:(NSString *)key contentType:(NSString *)contentType boundary:(NSString *) boundary filename:(NSString *)filename {
NSMutableData *postBody = [NSMutableData data];
[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\";\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Type: text\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[value dataUsingEncoding:NSUTF8StringEncoding]];
// There's certainly a better way to check if we are supposed to send binary data here.
if (filename){
[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", key, filename] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Transfer-Encoding: binary\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
} else {
[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
}
[postBody appendData:value];
[postBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
return postBody;

View File

@ -37,6 +37,7 @@
#if HOCKEYSDK_FEATURE_CRASH_REPORTER
#import "BITCrashManager.h"
#import "BITCrashAttachment.h"
#import "BITCrashManagerDelegate.h"
#endif /* HOCKEYSDK_FEATURE_CRASH_REPORTER */

View File

@ -127,6 +127,8 @@
1EAF20AA162DC0F600957B1D /* feedbackActiviy.png in Resources */ = {isa = PBXBuildFile; fileRef = 1EAF20A6162DC0F600957B1D /* feedbackActiviy.png */; };
1EAF20AB162DC0F600957B1D /* feedbackActiviy@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1EAF20A7162DC0F600957B1D /* feedbackActiviy@2x.png */; };
1EB52FD5167B766100C801D5 /* HockeySDK.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1E59555F15B6F80E00A03429 /* HockeySDK.strings */; };
1ED570C718BF878C00AB3350 /* BITCrashAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */; };
1ED570C818BF878C00AB3350 /* BITCrashAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ED570C618BF878C00AB3350 /* BITCrashAttachment.m */; };
1EF95CA6162CB037000AE3AD /* BITFeedbackActivity.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EF95CA4162CB036000AE3AD /* BITFeedbackActivity.h */; settings = {ATTRIBUTES = (Public, ); }; };
1EF95CA7162CB037000AE3AD /* BITFeedbackActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EF95CA5162CB036000AE3AD /* BITFeedbackActivity.m */; };
1EF95CAA162CB314000AE3AD /* BITFeedbackComposeViewControllerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EF95CA9162CB313000AE3AD /* BITFeedbackComposeViewControllerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -283,6 +285,8 @@
1EAF20A6162DC0F600957B1D /* feedbackActiviy.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = feedbackActiviy.png; sourceTree = "<group>"; };
1EAF20A7162DC0F600957B1D /* feedbackActiviy@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "feedbackActiviy@2x.png"; sourceTree = "<group>"; };
1EB52FC3167B73D400C801D5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/HockeySDK.strings; sourceTree = "<group>"; };
1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITCrashAttachment.h; sourceTree = "<group>"; };
1ED570C618BF878C00AB3350 /* BITCrashAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITCrashAttachment.m; sourceTree = "<group>"; };
1EDA60CF15C2C1450032D10B /* HockeySDK-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HockeySDK-Info.plist"; sourceTree = "<group>"; };
1EF95CA4162CB036000AE3AD /* BITFeedbackActivity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITFeedbackActivity.h; sourceTree = "<group>"; };
1EF95CA5162CB036000AE3AD /* BITFeedbackActivity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITFeedbackActivity.m; sourceTree = "<group>"; };
@ -480,6 +484,8 @@
1E754E571621FBB70070AB92 /* BITCrashManager.m */,
1EFF03D717F20F8300A5F13C /* BITCrashManagerPrivate.h */,
1E754E581621FBB70070AB92 /* BITCrashManagerDelegate.h */,
1ED570C518BF878C00AB3350 /* BITCrashAttachment.h */,
1ED570C618BF878C00AB3350 /* BITCrashAttachment.m */,
1E754E5A1621FBB70070AB92 /* BITCrashReportTextFormatter.h */,
1E754E5B1621FBB70070AB92 /* BITCrashReportTextFormatter.m */,
);
@ -633,6 +639,7 @@
1E59559B15B6FDA500A03429 /* HockeySDK.h in Headers */,
1E59559A15B6FDA500A03429 /* BITHockeyManager.h in Headers */,
1E5955FD15B7877B00A03429 /* BITHockeyManagerDelegate.h in Headers */,
1ED570C718BF878C00AB3350 /* BITCrashAttachment.h in Headers */,
1E754E5C1621FBB70070AB92 /* BITCrashManager.h in Headers */,
1E754E5E1621FBB70070AB92 /* BITCrashManagerDelegate.h in Headers */,
1E49A4731612226D00463151 /* BITUpdateManager.h in Headers */,
@ -876,6 +883,7 @@
1E49A4511612223B00463151 /* BITFeedbackManager.m in Sources */,
E4933E8117B66CDA00B11ACC /* BITHTTPOperation.m in Sources */,
1E49A45A1612223B00463151 /* BITFeedbackMessage.m in Sources */,
1ED570C818BF878C00AB3350 /* BITCrashAttachment.m in Sources */,
1E49A4601612223B00463151 /* BITFeedbackUserDataViewController.m in Sources */,
1E49A4701612226D00463151 /* BITAppVersionMetaInfo.m in Sources */,
1E49A4761612226D00463151 /* BITUpdateManager.m in Sources */,

View File

@ -201,6 +201,7 @@
[_sut handleCrashReport];
[verifyCount(delegateMock, times(1)) applicationLogForCrashManager:_sut];
[verifyCount(delegateMock, times(1)) attachmentForCrashManager:_sut];
// we should have now 1 pending crash report
assertThatBool([_sut hasPendingCrashReport], equalToBool(YES));