Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I unit test for EXC_BAD_ACCESS?

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?

like image 635
greenisus Avatar asked Nov 12 '11 21:11

greenisus


People also ask

How do you perform a unit test?

A typical unit test contains 3 phases: First, it initializes a small piece of an application it wants to test (also known as the system under test, or SUT), then it applies some stimulus to the system under test (usually by calling a method on it), and finally, it observes the resulting behavior.

How do you unit test go channels?

create a loop that will send data to the values channel. close the channel so that o. sum() returns. listen to the done channel so I know the function has finished processing the last value before I make any assertions.

What does thread 1 Exc_bad_access mean?

What does EXC_BAD_ACCESS mean? EXC_BAD_ACCESS is an exception raised as a result of accessing bad memory. We're constantly working with pointers to memory in Swift that link to a specific memory address. An application will crash whenever we try to access a pointer that is invalid or no longer exists.


2 Answers

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.

like image 91
jscs Avatar answered Oct 11 '22 14:10

jscs


I would suggest using one of the assertion macros found in the Assertions and Logging Programming Guide

So you could do something like:

NSAssert(myBlock != nil, @"myBlock must not be nil")

This enforces the preconditions that must be met before the method continues executing. It also allows the app to crash and will give you a reason why other than EXEC_BAD_ACCESS.

like image 21
Brandon A Avatar answered Oct 11 '22 12:10

Brandon A