erikdoe / ocmock

Mock objects for Objective-C
http://ocmock.org
Apache License 2.0
2.16k stars 606 forks source link

Mocks crashing due to relative addressing #440

Open dmaclach opened 4 years ago

dmaclach commented 4 years ago
- (void)testMockView {
  UIViewController *controller = [[UIViewController alloc] init];
  UIViewController *controller2 = [[UIViewController alloc] init];
  id mockController2 = OCMPartialMock(controller2);
  [controller addChildViewController:mockController2];
}

will crash with libGuardMalloc turned on. I'm guessing that the problem is that addChildViewController: is directly instance variables with its knowledge of UIViewController using offsets relative to self which in this case is a mock and not the controller itself. This ends up being a weird looking crash that is a pain to diagnose, and could end up "working" but corrupting the heap in weird and wonderful ways.

The problem can be avoided by not passing in the mock object. Should we be issuing a warning where partial mocks are being used instead of the object that they are mocking (I'm thinking -[OCMPartialMockObject handleUnRecordedInvocation:])

dmaclach commented 4 years ago

BTW I forgot to mention that this is an issue with 3.6 as well as head.

dmaclach commented 4 years ago

So here's an example that removes UIViewController and shows that this is not just a partial mock issue:

@interface TestClassLargeClass : NSObject {
  int foo[4096];
}
@end

@implementation TestClassLargeClass

- (void)messWithClass:(TestClassLargeClass *)cls
{
  for(int i = 0; i < 4096; ++i) {
    cls->foo[i] = i;
  }
}

@end

...

- (void)testClassLargeClass {
  TestClassLargeClass *one = [[TestClassLargeClass alloc] init];
  id mocktwo = OCMClassMock([TestClassLargeClass class]);
  [one messWithClass:mocktwo];
}

It can happen with any class that knows the internals of another. It's pretty nasty actually because it can cause very subtle memory corruption that shows up later in other tests.