blob: 2ea103c6943ed7ae4ca40a0f41720d8b49ee5e13 [file] [log] [blame]
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// BreakpadFramework_Test.mm
// Test case file for Breakpad.h/mm.
//
#import "GTMSenTestCase.h"
#import "Breakpad.h"
#include <mach/mach.h>
@interface BreakpadFramework_Test : GTMTestCase {
@private
int last_exception_code_;
int last_exception_type_;
mach_port_t last_exception_thread_;
// We're not using Obj-C BOOL because we need to interop with
// Breakpad's callback.
bool shouldHandleException_;
}
// This method is used by a callback used by test cases to determine
// whether to return true or false to Breakpad when handling an
// exception.
- (bool)shouldHandleException;
// This method returns a minimal dictionary that has what
// Breakpad needs to initialize.
- (NSMutableDictionary *)breakpadInitializationDictionary;
// This method is used by the exception handling callback
// to communicate to test cases the properites of the last
// exception.
- (void)setLastExceptionType:(int)type andCode:(int)code
andThread:(mach_port_t)thread;
@end
// Callback for Breakpad exceptions
bool myBreakpadCallback(int exception_type,
int exception_code,
mach_port_t crashing_thread,
void *context);
bool myBreakpadCallback(int exception_type,
int exception_code,
mach_port_t crashing_thread,
void *context) {
BreakpadFramework_Test *testCaseClass =
(BreakpadFramework_Test *)context;
[testCaseClass setLastExceptionType:exception_type
andCode:exception_code
andThread:crashing_thread];
bool shouldHandleException =
[testCaseClass shouldHandleException];
NSLog(@"Callback returning %d", shouldHandleException);
return shouldHandleException;
}
const int kNoLastExceptionCode = -1;
const int kNoLastExceptionType = -1;
const mach_port_t kNoLastExceptionThread = MACH_PORT_NULL;
@implementation BreakpadFramework_Test
- (void) initializeExceptionStateVariables {
last_exception_code_ = kNoLastExceptionCode;
last_exception_type_ = kNoLastExceptionType;
last_exception_thread_ = kNoLastExceptionThread;
}
- (NSMutableDictionary *)breakpadInitializationDictionary {
NSMutableDictionary *breakpadParams =
[NSMutableDictionary dictionaryWithCapacity:3];
[breakpadParams setObject:@"UnitTests" forKey:@BREAKPAD_PRODUCT];
[breakpadParams setObject:@"1.0" forKey:@BREAKPAD_VERSION];
[breakpadParams setObject:@"http://staging" forKey:@BREAKPAD_URL];
return breakpadParams;
}
- (bool)shouldHandleException {
return shouldHandleException_;
}
- (void)setLastExceptionType:(int)type
andCode:(int)code
andThread:(mach_port_t)thread {
last_exception_type_ = type;
last_exception_code_ = code;
last_exception_thread_ = thread;
}
// Test that the parameters mark required actually enable Breakpad to
// be initialized.
- (void)testBreakpadInstantiationWithRequiredParameters {
BreakpadRef b = BreakpadCreate([self breakpadInitializationDictionary]);
STAssertNotNULL(b, @"BreakpadCreate failed with required parameters");
BreakpadRelease(b);
}
// Test that Breakpad fails to initialize cleanly when required
// parameters are not present.
- (void)testBreakpadInstantiationWithoutRequiredParameters {
NSMutableDictionary *breakpadDictionary =
[self breakpadInitializationDictionary];
// Skip setting version, so that BreakpadCreate fails.
[breakpadDictionary removeObjectForKey:@BREAKPAD_VERSION];
BreakpadRef b = BreakpadCreate(breakpadDictionary);
STAssertNULL(b, @"BreakpadCreate did not fail when missing a required"
" parameter!");
breakpadDictionary = [self breakpadInitializationDictionary];
// Now test with no product
[breakpadDictionary removeObjectForKey:@BREAKPAD_PRODUCT];
b = BreakpadCreate(breakpadDictionary);
STAssertNULL(b, @"BreakpadCreate did not fail when missing a required"
" parameter!");
breakpadDictionary = [self breakpadInitializationDictionary];
// Now test with no URL
[breakpadDictionary removeObjectForKey:@BREAKPAD_URL];
b = BreakpadCreate(breakpadDictionary);
STAssertNULL(b, @"BreakpadCreate did not fail when missing a required"
" parameter!");
BreakpadRelease(b);
}
// Test to ensure that when we call BreakpadAddUploadParameter,
// it's added to the dictionary correctly(this test depends on
// some internal details of Breakpad, namely, the special prefix
// that it uses to figure out which key/value pairs to upload).
- (void)testAddingBreakpadServerVariable {
NSMutableDictionary *breakpadDictionary =
[self breakpadInitializationDictionary];
BreakpadRef b = BreakpadCreate(breakpadDictionary);
STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!");
BreakpadAddUploadParameter(b,
@"key",
@"value");
// Test that it did not add the key/value directly, e.g. without
// prepending the key with the prefix.
STAssertNil(BreakpadKeyValue(b, @"key"),
@"AddUploadParameter added key directly to dictionary"
" instead of prepending it!");
NSString *prependedKeyname =
[@BREAKPAD_SERVER_PARAMETER_PREFIX stringByAppendingString:@"key"];
STAssertEqualStrings(BreakpadKeyValue(b, prependedKeyname),
@"value",
@"Calling BreakpadAddUploadParameter did not prepend "
"key name");
BreakpadRelease(b);
}
// Test that when we do on-demand minidump generation,
// the exception code/type/thread are set properly.
- (void)testFilterCallbackReturnsFalse {
NSMutableDictionary *breakpadDictionary =
[self breakpadInitializationDictionary];
BreakpadRef b = BreakpadCreate(breakpadDictionary);
STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!");
BreakpadSetFilterCallback(b, &myBreakpadCallback, self);
// This causes the callback to return false, meaning
// Breakpad won't take the exception
shouldHandleException_ = false;
[self initializeExceptionStateVariables];
STAssertEquals(last_exception_type_, kNoLastExceptionType,
@"Last exception type not initialized correctly.");
STAssertEquals(last_exception_code_, kNoLastExceptionCode,
@"Last exception code not initialized correctly.");
STAssertEquals(last_exception_thread_, kNoLastExceptionThread,
@"Last exception thread is not initialized correctly.");
// Cause Breakpad's exception handler to be invoked.
BreakpadGenerateAndSendReport(b);
STAssertEquals(last_exception_type_, 0,
@"Last exception type is not 0 for on demand");
STAssertEquals(last_exception_code_, 0,
@"Last exception code is not 0 for on demand");
STAssertEquals(last_exception_thread_, mach_thread_self(),
@"Last exception thread is not mach_thread_self() "
"for on demand");
}
@end