#import <XCTest/XCTest.h>
#import "ObjectiveChipmunk/ObjectiveChipmunk.h"
@interface CallbacksTest : XCTestCase {}
@end
@implementation CallbacksTest
static cpBool
Begin(cpArbiter *arb, cpSpace *space, NSMutableString *string){
[string appendString:@"Begin-"];
return cpTrue;
}
static cpBool
PreSolve(cpArbiter *arb, cpSpace *space, NSMutableString *string){
[string appendString:@"PreSolve-"];
return cpTrue;
}
static void
PostSolve(cpArbiter *arb, cpSpace *space, NSMutableString *string){
[string appendString:@"PostSolve-"];
}
static void
Separate(cpArbiter *arb, cpSpace *space, NSMutableString *string){
[string appendString:@"Separate-"];
}
static void
testHandlersHelper(id self, bool separateByRemove, bool enableContactGraph){
ChipmunkSpace *space = [[ChipmunkSpace alloc] init];
space.collisionBias = 1.0f;
cpFloat radius = 5;
ChipmunkBody *body1 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]];
body1.position = cpv(0*radius*1.5,0);
[space add:[ChipmunkCircleShape circleWithBody:body1 radius:radius offset:cpvzero]];
ChipmunkBody *body2 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]];
body2.position = cpv(1*radius*1.5,0);
ChipmunkShape *shape2 = [space add:[ChipmunkCircleShape circleWithBody:body2 radius:radius offset:cpvzero]];
NSMutableString *string = [NSMutableString string];
cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space.space, nil, nil);
handler->beginFunc = (cpCollisionBeginFunc)Begin,
handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve,
handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve,
handler->separateFunc = (cpCollisionSeparateFunc)Separate,
handler->userData = string;
[space step:0.1];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-", @"");
[space step:0.1];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-PostSolve-", @"");
if(separateByRemove){
[space remove:shape2];
} else {
body2.position = cpv(100, 100);
[space step:0.1];
}
XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-PostSolve-Separate-", @"");
[space step:0.1];
[space release];
}
-(void)testHandlers {
testHandlersHelper(self, true, true);
testHandlersHelper(self, false, true);
testHandlersHelper(self, false, false);
testHandlersHelper(self, true, false);
}
static void
testHandlersSleepingHelper(id self, int wakeRemoveType){
ChipmunkSpace *space = [[ChipmunkSpace alloc] init];
space.collisionBias = 1.0f;
space.sleepTimeThreshold = 0.15;
NSString *type = @"type";
cpFloat radius = 5;
ChipmunkBody *body1 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]];
body1.position = cpv(0*radius*1.5,0);
ChipmunkShape *shape1 = [space add:[ChipmunkCircleShape circleWithBody:body1 radius:radius offset:cpvzero]];
shape1.collisionType = type;
ChipmunkBody *body2 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]];
body2.position = cpv(1*radius*1.5,0);
ChipmunkShape *shape2 = [space add:[ChipmunkCircleShape circleWithBody:body2 radius:radius offset:cpvzero]];
shape2.collisionType = type;
NSMutableString *string = [NSMutableString string];
cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space.space, type, type);
handler->beginFunc = (cpCollisionBeginFunc)Begin,
handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve,
handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve,
handler->separateFunc = (cpCollisionSeparateFunc)Separate,
handler->userData = string;
[space step:0.1];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-", @"");
[space step:0.1];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-", @"");
[space step:0.1];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-", @"");
switch(wakeRemoveType){
case 0:
[space remove:shape2];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-Separate-", @"");
break;
case 1:
body2.position = cpv(100, 100);
[space step:0.1];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-Separate-", @"");
break;
default:break;
}
[space step:0.1];
[space release];
}
-(void)testHandlersSleeping {
testHandlersSleepingHelper(self, 0);
testHandlersSleepingHelper(self, 1);
}
static void
testSleepingSensorCallbacksHelper(id self, int wakeRemoveType){
ChipmunkSpace *space = [[ChipmunkSpace alloc] init];
space.collisionBias = 1.0f;
space.sleepTimeThreshold = 0.15;
cpFloat radius = 5;
ChipmunkBody *body1 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]];
body1.position = cpv(0*radius*1.5,0);
[space add:[ChipmunkCircleShape circleWithBody:body1 radius:radius offset:cpvzero]];
ChipmunkBody *body2 = [space add:[ChipmunkBody staticBody]];
body2.position = cpv(1*radius*1.5,0);
ChipmunkShape *shape2 = [space add:[ChipmunkCircleShape circleWithBody:body2 radius:radius offset:cpvzero]];
shape2.sensor = true;
NSMutableString *string = [NSMutableString string];
cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space.space, nil, nil);
handler->beginFunc = (cpCollisionBeginFunc)Begin,
handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve,
handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve,
handler->separateFunc = (cpCollisionSeparateFunc)Separate,
handler->userData = string;
[space step:0.1];
XCTAssertEqualObjects(string, @"Begin-PreSolve-", @"");
[space step:0.1];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PreSolve-", @"");
[space step:0.1];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PreSolve-", @"");
switch(wakeRemoveType){
case 0:
[space remove:shape2];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PreSolve-Separate-", @"");
break;
case 1:
body1.position = cpv(100, 100);
[space step:0.1];
XCTAssertEqualObjects(string, @"Begin-PreSolve-PreSolve-Separate-", @"");
break;
default:break;
}
[space step:0.1];
[space release];
}
-(void)testSleepingSensorCallbacks {
testSleepingSensorCallbacksHelper(self, 0);
testSleepingSensorCallbacksHelper(self, 1);
}
static cpBool CallBlock(cpArbiter *arb, cpSpace *space, cpBool (^block)(cpArbiter *arb)){return block(arb);}
-(void)testPostStepRemoval {
NSString *ballType = @"ballType";
NSString *barType = @"barType";
ChipmunkSpace *space = [[ChipmunkSpace alloc] init];
space.gravity = cpv(0, -100);
cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space.space, ballType, barType);
handler->beginFunc = (cpCollisionBeginFunc)CallBlock,
handler->userData = ^(cpArbiter *arb){
CHIPMUNK_ARBITER_GET_SHAPES(arb, ballShape, barShape);
[space addPostStepRemoval:barShape];
return TRUE;
};
ChipmunkShape *shape;
shape = [space add:[ChipmunkSegmentShape segmentWithBody:space.staticBody from:cpv(-10,0) to:cpv(10,0) radius:1]];
shape = [space add:[ChipmunkSegmentShape segmentWithBody:space.staticBody from:cpv(-10,2) to:cpv(10,2) radius:1]];
shape.collisionType = barType;
ChipmunkBody *ball = [space add:[ChipmunkBody bodyWithMass:1 andMoment:cpMomentForCircle(1, 0, 1, cpvzero)]];
ball.position = cpv(0, 10);
shape = [space add:[ChipmunkCircleShape circleWithBody:ball radius:1 offset:cpvzero]];
shape.collisionType = ballType;
for(int i=0; i<100; i++) [space step:0.01];
XCTAssertEqualWithAccuracy(ball.position.y, (cpFloat)2, 1.1*space.collisionSlop, @"");
[space release];
}
-(void)testPostStepFromPostStep
{
ChipmunkSpace *space = [[ChipmunkSpace alloc] init];
ChipmunkShape *staticShape = [space add:[ChipmunkCircleShape circleWithBody:space.staticBody radius:1.0 offset:cpvzero]];
staticShape.collisionType = staticShape;
ChipmunkBody *body1 = [space add:[ChipmunkBody kinematicBody]];
ChipmunkShape *shape1 = [space add:[ChipmunkCircleShape circleWithBody:body1 radius:1.0 offset:cpvzero]];
shape1.collisionType = shape1;
shape1.sensor = TRUE;
ChipmunkBody *body2 = [space add:[ChipmunkBody kinematicBody]];
ChipmunkShape *shape2 = [space add:[ChipmunkCircleShape circleWithBody:body2 radius:10.0 offset:cpvzero]];
shape2.collisionType = shape2;
shape2.sensor = TRUE;
__block bool trigger1 = FALSE;
__block bool trigger2 = FALSE;
__block bool trigger3 = FALSE;
__block bool trigger4 = FALSE;
__block bool trigger5 = FALSE;
__block bool trigger6 = FALSE;
__block bool trigger7 = FALSE;
cpCollisionHandler *handler1 = cpSpaceAddCollisionHandler(space.space, staticShape, shape1);
handler1->separateFunc = (cpCollisionSeparateFunc)CallBlock,
handler1->userData = ^(cpArbiter *arb){
XCTAssertTrue(cpSpaceIsLocked(space.space), @"");
[space addPostStepBlock:^{
XCTAssertFalse(cpSpaceIsLocked(space.space), @"");
[space remove:shape1];
trigger2 = TRUE;
} key:shape1];
trigger1 = TRUE;
};
cpCollisionHandler *handler2 = cpSpaceAddCollisionHandler(space.space, shape1, shape2);
handler2->separateFunc = (cpCollisionSeparateFunc)CallBlock,
handler2->userData = ^(cpArbiter *arb){
XCTAssertTrue(cpSpaceIsLocked(space.space), @"");
[space addPostStepBlock:^{trigger4 = TRUE;} key:shape1];
trigger3 = TRUE;
};
cpCollisionHandler *handler3 = cpSpaceAddCollisionHandler(space.space, staticShape, shape2);
handler3->separateFunc = (cpCollisionSeparateFunc)CallBlock,
handler3->userData = ^(cpArbiter *arb){
XCTAssertTrue(cpSpaceIsLocked(space.space), @"");
[space addPostStepBlock:^{
[space addPostStepBlock:^{trigger7 = TRUE;} key:shape2];
trigger6 = TRUE;
} key:shape2];
trigger5 = TRUE;
};
[space step:1.0];
body1.position = cpv(10, 0);
[space step:1.0];
XCTAssertTrue(trigger1, @"");
XCTAssertTrue(trigger2, @"");
XCTAssertTrue(trigger3, @"");
XCTAssertFalse(trigger4, @"");
[space remove:shape2];
XCTAssertTrue(trigger5, @"");
XCTAssertTrue(trigger6, @"");
XCTAssertFalse(trigger7, @"");
[space release];
}
-(void)testBlockIterators {
ChipmunkSpace *space = [[ChipmunkSpace alloc] init];
ChipmunkShape *_staticShape = [ChipmunkCircleShape circleWithBody:space.staticBody radius:1.0 offset:cpvzero];
ChipmunkBody *_body = [ChipmunkBody bodyWithMass:1.0 andMoment:1.0];
ChipmunkShape *_shape = [ChipmunkCircleShape circleWithBody:_body radius:1.0 offset:cpvzero];
ChipmunkConstraint *_constraint = [ChipmunkPivotJoint pivotJointWithBodyA:space.staticBody bodyB:_body pivot:cpvzero];
{
__block int counter = 0;
cpSpaceEachBody_b(space.space, ^(cpBody *body){
XCTAssertEqual(cpBodyGetUserData(body), _body, @"");
counter++;
});
cpSpaceEachShape_b(space.space, ^(cpShape *shape){
if(cpBodyGetType(cpShapeGetBody(shape)) == CP_BODY_TYPE_STATIC){
XCTAssertEqual(shape, _staticShape.shape, @"");
} else {
XCTAssertEqual(shape, _shape.shape, @"");
}
counter++;
});
cpSpaceEachConstraint_b(space.space, ^(cpConstraint *constraint){
XCTAssertEqual(constraint, _constraint.constraint, @"");
counter++;
});
cpBodyEachShape_b(space.staticBody.body, ^(cpShape *shape){
XCTAssertEqual(shape, _staticShape.shape, @"");
counter++;
});
cpBodyEachConstraint_b(space.staticBody.body, ^(cpConstraint *constraint){
XCTAssertEqual(constraint, _constraint.constraint, @"");
counter++;
});
cpBodyEachShape_b(_body.body, ^(cpShape *shape){
XCTAssertEqual(shape, _shape.shape, @"");
counter++;
});
cpBodyEachConstraint_b(_body.body, ^(cpConstraint *constraint){
XCTAssertEqual(constraint, _constraint.constraint, @"");
counter++;
});
XCTAssertEqual(counter, 0, @"");
}
[space add:_staticShape];
[space add:_body];
[space add:_shape];
[space add:_constraint];
{
__block int counter = 0;
cpSpaceEachBody_b(space.space, ^(cpBody *body){
XCTAssertEqual(body, _body.body, @"");
counter++;
});
cpSpaceEachShape_b(space.space, ^(cpShape *shape){
if(cpBodyGetType(cpShapeGetBody(shape)) == CP_BODY_TYPE_STATIC){
XCTAssertEqual(shape, _staticShape.shape, @"");
} else {
XCTAssertEqual(shape, _shape.shape, @"");
}
counter++;
});
cpSpaceEachConstraint_b(space.space, ^(cpConstraint *constraint){
XCTAssertEqual(constraint, _constraint.constraint, @"");
counter++;
});
cpBodyEachShape_b(space.staticBody.body, ^(cpShape *shape){
XCTAssertEqual(shape, _staticShape.shape, @"");
counter++;
});
cpBodyEachConstraint_b(space.staticBody.body, ^(cpConstraint *constraint){
XCTAssertEqual(constraint, _constraint.constraint, @"");
counter++;
});
cpBodyEachShape_b(_body.body, ^(cpShape *shape){
XCTAssertEqual(shape, _shape.shape, @"");
counter++;
});
cpBodyEachConstraint_b(_body.body, ^(cpConstraint *constraint){
XCTAssertEqual(constraint, _constraint.constraint, @"");
counter++;
});
XCTAssertEqual(counter, 8, @"");
}
[space release];
}
@end