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 64 bool myBreakpadCallback(int exception_type, 65 int exception_code, 66 mach_port_t crashing_thread, 67 void *context); 68 69 bool 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 } 83 const int kNoLastExceptionCode = -1; 84 const int kNoLastExceptionType = -1; 85 const 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