1// Copyright 2009 Google LLC 2// 3// Redistribution and use in source and binary forms, with or without 4// modification, are permitted provided that the following conditions are 5// met: 6// 7// * Redistributions of source code must retain the above copyright 8// notice, this list of conditions and the following disclaimer. 9// * Redistributions in binary form must reproduce the above 10// copyright notice, this list of conditions and the following disclaimer 11// in the documentation and/or other materials provided with the 12// distribution. 13// * Neither the name of Google LLC nor the names of its 14// contributors may be used to endorse or promote products derived from 15// this software without specific prior written permission. 16// 17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28// 29// BreakpadFramework_Test.mm 30// Test case file for Breakpad.h/mm. 31// 32 33#import "GTMSenTestCase.h" 34#import "Breakpad.h" 35 36#include <mach/mach.h> 37 38@interface BreakpadFramework_Test : GTMTestCase { 39 @private 40 int last_exception_code_; 41 int last_exception_type_; 42 mach_port_t last_exception_thread_; 43 // We're not using Obj-C BOOL because we need to interop with 44 // Breakpad's callback. 45 bool shouldHandleException_; 46} 47 48// This method is used by a callback used by test cases to determine 49// whether to return true or false to Breakpad when handling an 50// exception. 51- (bool)shouldHandleException; 52// This method returns a minimal dictionary that has what 53// Breakpad needs to initialize. 54- (NSMutableDictionary *)breakpadInitializationDictionary; 55// This method is used by the exception handling callback 56// to communicate to test cases the properites of the last 57// exception. 58- (void)setLastExceptionType:(int)type andCode:(int)code 59 andThread:(mach_port_t)thread; 60@end 61 62// Callback for Breakpad exceptions 63bool myBreakpadCallback(int exception_type, 64 int exception_code, 65 mach_port_t crashing_thread, 66 void *context); 67 68bool myBreakpadCallback(int exception_type, 69 int exception_code, 70 mach_port_t crashing_thread, 71 void *context) { 72 BreakpadFramework_Test *testCaseClass = 73 (BreakpadFramework_Test *)context; 74 [testCaseClass setLastExceptionType:exception_type 75 andCode:exception_code 76 andThread:crashing_thread]; 77 bool shouldHandleException = 78 [testCaseClass shouldHandleException]; 79 NSLog(@"Callback returning %d", shouldHandleException); 80 return shouldHandleException; 81} 82const int kNoLastExceptionCode = -1; 83const int kNoLastExceptionType = -1; 84const mach_port_t kNoLastExceptionThread = MACH_PORT_NULL; 85 86@implementation BreakpadFramework_Test 87- (void) initializeExceptionStateVariables { 88 last_exception_code_ = kNoLastExceptionCode; 89 last_exception_type_ = kNoLastExceptionType; 90 last_exception_thread_ = kNoLastExceptionThread; 91} 92 93- (NSMutableDictionary *)breakpadInitializationDictionary { 94 NSMutableDictionary *breakpadParams = 95 [NSMutableDictionary dictionaryWithCapacity:3]; 96 97 [breakpadParams setObject:@"UnitTests" forKey:@BREAKPAD_PRODUCT]; 98 [breakpadParams setObject:@"1.0" forKey:@BREAKPAD_VERSION]; 99 [breakpadParams setObject:@"http://staging" forKey:@BREAKPAD_URL]; 100 return breakpadParams; 101} 102 103- (bool)shouldHandleException { 104 return shouldHandleException_; 105} 106 107- (void)setLastExceptionType:(int)type 108 andCode:(int)code 109 andThread:(mach_port_t)thread { 110 last_exception_type_ = type; 111 last_exception_code_ = code; 112 last_exception_thread_ = thread; 113} 114 115// Test that the parameters mark required actually enable Breakpad to 116// be initialized. 117- (void)testBreakpadInstantiationWithRequiredParameters { 118 BreakpadRef b = BreakpadCreate([self breakpadInitializationDictionary]); 119 STAssertNotNULL(b, @"BreakpadCreate failed with required parameters"); 120 BreakpadRelease(b); 121} 122 123// Test that Breakpad fails to initialize cleanly when required 124// parameters are not present. 125- (void)testBreakpadInstantiationWithoutRequiredParameters { 126 NSMutableDictionary *breakpadDictionary = 127 [self breakpadInitializationDictionary]; 128 129 // Skip setting version, so that BreakpadCreate fails. 130 [breakpadDictionary removeObjectForKey:@BREAKPAD_VERSION]; 131 BreakpadRef b = BreakpadCreate(breakpadDictionary); 132 STAssertNULL(b, @"BreakpadCreate did not fail when missing a required" 133 " parameter!"); 134 135 breakpadDictionary = [self breakpadInitializationDictionary]; 136 // Now test with no product 137 [breakpadDictionary removeObjectForKey:@BREAKPAD_PRODUCT]; 138 b = BreakpadCreate(breakpadDictionary); 139 STAssertNULL(b, @"BreakpadCreate did not fail when missing a required" 140 " parameter!"); 141 142 breakpadDictionary = [self breakpadInitializationDictionary]; 143 // Now test with no URL 144 [breakpadDictionary removeObjectForKey:@BREAKPAD_URL]; 145 b = BreakpadCreate(breakpadDictionary); 146 STAssertNULL(b, @"BreakpadCreate did not fail when missing a required" 147 " parameter!"); 148 BreakpadRelease(b); 149} 150 151// Test to ensure that when we call BreakpadAddUploadParameter, 152// it's added to the dictionary correctly(this test depends on 153// some internal details of Breakpad, namely, the special prefix 154// that it uses to figure out which key/value pairs to upload). 155- (void)testAddingBreakpadServerVariable { 156 NSMutableDictionary *breakpadDictionary = 157 [self breakpadInitializationDictionary]; 158 159 BreakpadRef b = BreakpadCreate(breakpadDictionary); 160 STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!"); 161 162 BreakpadAddUploadParameter(b, 163 @"key", 164 @"value"); 165 166 // Test that it did not add the key/value directly, e.g. without 167 // prepending the key with the prefix. 168 STAssertNil(BreakpadKeyValue(b, @"key"), 169 @"AddUploadParameter added key directly to dictionary" 170 " instead of prepending it!"); 171 172 NSString *prependedKeyname = 173 [@BREAKPAD_SERVER_PARAMETER_PREFIX stringByAppendingString:@"key"]; 174 175 STAssertEqualStrings(BreakpadKeyValue(b, prependedKeyname), 176 @"value", 177 @"Calling BreakpadAddUploadParameter did not prepend " 178 "key name"); 179 BreakpadRelease(b); 180} 181 182// Test that when we do on-demand minidump generation, 183// the exception code/type/thread are set properly. 184- (void)testFilterCallbackReturnsFalse { 185 NSMutableDictionary *breakpadDictionary = 186 [self breakpadInitializationDictionary]; 187 188 BreakpadRef b = BreakpadCreate(breakpadDictionary); 189 STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!"); 190 BreakpadSetFilterCallback(b, &myBreakpadCallback, self); 191 192 // This causes the callback to return false, meaning 193 // Breakpad won't take the exception 194 shouldHandleException_ = false; 195 196 [self initializeExceptionStateVariables]; 197 STAssertEquals(last_exception_type_, kNoLastExceptionType, 198 @"Last exception type not initialized correctly."); 199 STAssertEquals(last_exception_code_, kNoLastExceptionCode, 200 @"Last exception code not initialized correctly."); 201 STAssertEquals(last_exception_thread_, kNoLastExceptionThread, 202 @"Last exception thread is not initialized correctly."); 203 204 // Cause Breakpad's exception handler to be invoked. 205 BreakpadGenerateAndSendReport(b); 206 207 STAssertEquals(last_exception_type_, 0, 208 @"Last exception type is not 0 for on demand"); 209 STAssertEquals(last_exception_code_, 0, 210 @"Last exception code is not 0 for on demand"); 211 STAssertEquals(last_exception_thread_, mach_thread_self(), 212 @"Last exception thread is not mach_thread_self() " 213 "for on demand"); 214} 215 216@end 217