xref: /aosp_15_r20/external/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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