I know how to resolve EXC_BAD_ACCESS issues, but I'm not sure how to unit test for it. Is there a way to capture EXC_BAD_ACCESS in code instead of simply crashing?
Here's why I ask: I have written a library that heavily uses blocks, like this:
- (void)doSomething:(void (^)())myBlock;
In my implementation of doSomething:
I'm going to eventually run the block, like this:
myBlock();
If a caller passes nil for the block, then it will crash with EXC_BAD_ACCESS
, so the solution is to check that the block exists, like this:
if (myBlock) {
myBlock();
}
This nil check is pretty easy to forget, so I'd like a way to write a unit test that fails when the crash occurs. I suppose a crash could be considered a test failure, but I think it would be nicer for others trying to run the tests to see a nice failure message rather than a crash. Any ideas?
I think you'll need to run the test in a subprocess; then you can let the subprocess crash, check for that crash, and fail the test neatly if it occurs.
Working from Peter Hosey's singleton test code.
- (void) runTestInSubprocess:(SEL)testCmd {
pid_t pid = fork();
// The return value of fork is 0 in the child process, and it is
// the id of the child process in the parent process.
if (pid == 0) {
// Child process: run test
// isInSubprocess is an ivar of your test case class
isInSubprocess = YES;
[self performSelector:testCmd];
exit(0);
} else {
// Parent process: wait for child process to end, check
// its status
int status;
waitpid(pid, &status, /*options*/ 0);
// This was a crash; fail the test
STAssertFalse(WIFSIGNALED(status), @"Test %@ crashed due to signal %d", NSStringFromSelector(testCmd), WTERMSIG(status));
}
}
Each test will then run itself in a subprocess like so:
- (void) testSomething {
if (!isInSubprocess) {
// Hand off this test's selector to be run in a subprocess
[self runTestInSubprocess:_cmd];
return;
}
// Put actual test code here
STAssertEquals(1, 1, @"Something wrong with the universe.");
}
You may need to tweak this; I haven't tested it.