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