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