/ src / client / mac / tests / BreakpadFramework_Test.mm
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