You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

687 lines
25 KiB

/**
Copyright (c) 2014-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
*/
#import <OCMock/OCMock.h>
#import <QuartzCore/QuartzCore.h>
#import <XCTest/XCTest.h>
#import <pop/POPAnimation.h>
#import <pop/POPAnimationPrivate.h>
#import <pop/POPAnimator.h>
#import <pop/POPAnimatorPrivate.h>
#import <pop/POPAnimationExtras.h>
#import "POPAnimatable.h"
#import "POPAnimationInternal.h"
#import "POPAnimationTestsExtras.h"
#import "POPBaseAnimationTests.h"
#import "POPCGUtils.h"
@interface POPSpringAnimationTests : POPBaseAnimationTests
@end
@implementation POPSpringAnimationTests
static NSString *animationKey = @"key";
- (POPSpringAnimation *)_positionAnimation
{
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.fromValue = @0.0;
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.springBounciness = 4.0;
return anim;
}
- (void)testCompletion
{
// animation
// the default from, to and bounciness values are used
NSArray *markers = @[@0.5, @0.75, @1.0];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPosition];
anim.progressMarkers = markers;
XCTAssertEqualObjects(markers, anim.progressMarkers, @"%@ shoudl equal %@", markers, anim.progressMarkers);
// delegate
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// expect start, progress & stop to all be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
anim.delegate = delegate;
// layer
id layer = [OCMockObject niceMockForClass:[CALayer class]];
// expect position to be called
CGPoint position = CGPointMake(100, 100);
position = [(CALayer *)[[layer stub] andReturnValue:OCMOCK_VALUE(position)] position];
[layer pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @0.1, @0.2]);
[layer verify];
[delegate verify];
}
- (void)testConvergence
{
POPAnimatable *circle = [POPAnimatable new];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.fromValue = @0.0;
anim.toValue = @100.0;
anim.velocity = @100.0;
anim.springBounciness = 0.5;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 1.0, 1.0/60.0);
[tracer stop];
// finished
POPAnimationValueEvent *stopEvent = [[tracer eventsWithType:kPOPAnimationEventDidStop] lastObject];
XCTAssertEqualObjects(stopEvent.value, @YES, @"unexpected stop event %@", stopEvent);
// convergence threshold
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
NSUInteger toValueFrameCount = POPAnimationCountLastEventValues(writeEvents, anim.toValue, anim.property.threshold);
XCTAssertTrue(toValueFrameCount < kPOPAnimationConvergenceMaxFrameCount, @"unexpected convergence; toValueFrameCount: %lu", (unsigned long)toValueFrameCount);
}
- (void)testConvergenceRounded
{
POPAnimatable *circle = [POPAnimatable new];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.fromValue = @0.0;
anim.toValue = @100.0;
anim.velocity = @100.0;
anim.springBounciness = 0.5;
anim.roundingFactor = 1.0;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 1.0, 1.0/60.0);
[tracer stop];
// finished
POPAnimationValueEvent *stopEvent = [[tracer eventsWithType:kPOPAnimationEventDidStop] lastObject];
XCTAssertEqualObjects(stopEvent.value, @YES, @"unexpected stop event %@", stopEvent);
// convergence threshold
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
NSUInteger toValueFrameCount = POPAnimationCountLastEventValues(writeEvents, anim.toValue);
XCTAssertTrue(toValueFrameCount < kPOPAnimationConvergenceMaxFrameCount, @"unexpected convergence; toValueFrameCount: %lu", (unsigned long)toValueFrameCount);
}
- (void)testConvergenceClampedRounded
{
POPAnimatable *circle = [POPAnimatable new];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.fromValue = @0.0;
anim.toValue = @100.0;
anim.velocity = @100.0;
anim.springBounciness = 0.5;
anim.roundingFactor = 1.0;
anim.clampMode = kPOPAnimationClampEnd;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[circle pop_addAnimation:anim forKey:@"key"];
POPAnimatorRenderDuration(self.animator, self.beginTime, 1.0, 1.0/60.0);
[tracer stop];
// finished
POPAnimationValueEvent *stopEvent = [[tracer eventsWithType:kPOPAnimationEventDidStop] lastObject];
XCTAssertEqualObjects(stopEvent.value, @YES, @"unexpected stop event %@", stopEvent);
// convergence threshold
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
NSUInteger toValueFrameCount = POPAnimationCountLastEventValues(writeEvents, anim.toValue);
XCTAssertTrue(toValueFrameCount < kPOPAnimationConvergenceMaxFrameCount, @"unexpected convergence; toValueFrameCount: %lu", (unsigned long)toValueFrameCount);
}
- (void)testRemovedOnCompletionNoStartStopBasics
{
CALayer *layer = self.layer1;
POPSpringAnimation *anim = self._positionAnimation;
POPAnimationTracer *tracer = anim.tracer;
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// cleanup
[layer pop_removeAllAnimations];
// configure animation
anim.removedOnCompletion = NO;
anim.toValue = @5.0;
anim.delegate = delegate;
__block BOOL completionBlock = NO;
__block BOOL completionBlockFinished = NO;
anim.completionBlock = ^(POPAnimation *a, BOOL finished) {
completionBlock = YES;
completionBlockFinished = finished;
};
// start tracer
[tracer start];
// expect start and stopped
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
[layer pop_addAnimation:anim forKey:animationKey];
POPAnimatorRenderDuration(self.animator, self.beginTime, 20.0, 1.0/60.0);
NSArray *allEvents = tracer.allEvents;
// verify delegate
[delegate verify];
XCTAssertTrue(completionBlock, @"completion block did not execute %@", allEvents);
XCTAssertTrue(completionBlockFinished, @"completion block did not finish %@", allEvents);
// assert animation has not been removed
XCTAssertTrue(anim == [layer pop_animationForKey:animationKey], @"expected animation on layer animations:%@", [layer pop_animationKeys]);
}
- (void)testRemovedOnCompletionNoContinuations
{
static NSString *animationKey = @"key";
static NSArray *toValues = @[@50.0, @100.0, @20.0, @80.0];
static NSArray *durations = @[@2.0, @0.3, @0.4, @2.0];
CALayer *layer = self.layer1;
POPSpringAnimation *anim = self._positionAnimation;
POPAnimationTracer *tracer = anim.tracer;
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
// cleanup
[layer pop_removeAllAnimations];
// configure animation
anim.removedOnCompletion = NO;
anim.delegate = delegate;
// start tracer
[tracer start];
__block CFTimeInterval beginTime;
__block BOOL completionBlock = NO;
__block BOOL completionBlockFinished = NO;
[toValues enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *ptrStop) {
anim.toValue = obj;
if (0 == idx) {
[tracer reset];
// starts and stops
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
anim.completionBlock = ^(POPAnimation *a, BOOL finished) {
completionBlock = YES;
completionBlockFinished = finished;
};
[layer pop_addAnimation:anim forKey:animationKey];
beginTime = self.beginTime;
CFTimeInterval dt = [durations[idx] doubleValue];
POPAnimatorRenderDuration(self.animator, beginTime, dt, 1.0/60.0);
beginTime += dt;
NSArray *allEvents = tracer.allEvents;
NSArray *didReachEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
// verify delegate
[delegate verify];
XCTAssertTrue(1 == didReachEvents.count, @"unexpected didReachEvents %@", didReachEvents);
XCTAssertTrue(completionBlock, @"completion block did not execute %@", allEvents);
XCTAssertTrue(completionBlockFinished, @"completion block did not finish %@", allEvents);
} else if (toValues.count - 1 == idx) {
// continue stoped animation
[tracer reset];
completionBlock = NO;
completionBlockFinished = NO;
[[delegate expect] pop_animationDidStop:anim finished:YES];
CFTimeInterval dt = [durations[idx] doubleValue];
POPAnimatorRenderDuration(self.animator, beginTime, dt, 1.0/60.0);
beginTime += dt;
NSArray *allEvents = tracer.allEvents;
NSArray *didReachEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
// verify delegate
[delegate verify];
XCTAssertTrue(1 == didReachEvents.count, @"unexpected didReachEvents %@", didReachEvents);
XCTAssertTrue(completionBlock, @"completion block did not execute %@", allEvents);
XCTAssertTrue(completionBlockFinished, @"completion block did not finish %@", allEvents);
} else {
// continue stoped (idx = 1) or started animation
if (1 == idx) {
[[delegate expect] pop_animationDidStart:anim];
}
// reset state
[tracer reset];
completionBlock = NO;
completionBlockFinished = NO;
CFTimeInterval dt = [durations[idx] doubleValue];
POPAnimatorRenderDuration(self.animator, beginTime, dt, 1.0/60.0);
beginTime += dt;
NSArray *allEvents = tracer.allEvents;
NSArray *didReachEvents = [tracer eventsWithType:kPOPAnimationEventDidReachToValue];
// verify delegate
[delegate verify];
XCTAssertTrue(1 == didReachEvents.count, @"unexpected didReachEvents %@", didReachEvents);
XCTAssertFalse(completionBlock, @"completion block did not execute %@ %@", anim, allEvents);
XCTAssertFalse(completionBlockFinished, @"completion block did not finish %@ %@", anim, allEvents);
}
// assert animation has not been removed
XCTAssertTrue(anim == [layer pop_animationForKey:animationKey], @"expected animation on layer animations:%@", [layer pop_animationKeys]);
}];
}
- (void)testNoOperationAnimation
{
const CGPoint initialValue = CGPointMake(100, 100);
CALayer *layer = self.layer1;
layer.position = initialValue;
[layer pop_removeAllAnimations];
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPosition];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// starts and stops
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
[layer pop_addAnimation:anim forKey:animationKey];
POPAnimatorRenderDuration(self.animator, self.beginTime, 5, 1.0/60.0);
// verify delegate
[delegate verify];
// verify number values
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
for (POPAnimationValueEvent *writeEvent in writeEvents) {
XCTAssertEqualObjects(writeEvent.value, [NSValue valueWithCGPoint:initialValue], @"unexpected write event:%@ anim:%@", writeEvent, anim);
}
}
- (void)testLazyValueInitialization
{
CALayer *layer = self.layer1;
layer.position = CGPointZero;
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
anim.fromValue = @100.0;
anim.beginTime = self.beginTime + 0.3;
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// add animation, but do not start
[layer pop_addAnimation:anim forKey:animationKey];
POPAnimatorRenderDuration(self.animator, self.beginTime, 0.2, 1.0/60.0);
XCTAssertNotNil(anim.fromValue, @"unexpected from value %@", anim);
XCTAssertNil(anim.toValue, @"unexpected to value %@", anim);
// start animation
POPAnimatorRenderDuration(self.animator, self.beginTime + 0.2, 0.2, 1.0/60.0);
XCTAssertNotNil(anim.fromValue, @"unexpected from value %@", anim);
XCTAssertNotNil(anim.toValue, @"unexpected to value %@", anim);
// continue running animation
anim.fromValue = nil;
anim.toValue = @200.0;
POPAnimatorRenderDuration(self.animator, self.beginTime + 0.4, 0.2, 1.0/60.0);
XCTAssertNotNil(anim.fromValue, @"unexpected from value %@", anim);
XCTAssertNotNil(anim.toValue, @"unexpected to value %@", anim);
}
- (void)testLatentSpring
{
POPSpringAnimation *translationAnimation = [POPSpringAnimation animation];
translationAnimation.dynamicsTension = 990;
translationAnimation.dynamicsFriction = 230;
translationAnimation.dynamicsMass = 1.0;
translationAnimation.property = [POPAnimatableProperty propertyWithName:kPOPLayerOpacity];
translationAnimation.removedOnCompletion = NO;
[self pop_addAnimation:translationAnimation forKey:@"test"];
POPAnimatorRenderDuration(self.animator, self.beginTime + 0.4, 0.2, 1.0/60.0);
}
- (void)testRectSupport
{
const CGRect fromRect = CGRectMake(0, 0, 0, 0);
const CGRect toRect = CGRectMake(100, 200, 200, 400);
const CGRect velocityRect = CGRectMake(1000, 1000, 1000, 1000);
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
anim.fromValue = [NSValue valueWithCGRect:fromRect];
anim.toValue = [NSValue valueWithCGRect:toRect];
anim.velocity = [NSValue valueWithCGRect:velocityRect];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// expect start and stop to be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify delegate
[delegate verify];
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
CGRect lastRect = [lastEvent.value CGRectValue];
// verify last rect is to rect
XCTAssertTrue(CGRectEqualToRect(lastRect, toRect), @"unexpected last rect value: %@", lastEvent);
}
#if TARGET_OS_IPHONE
- (void)testEdgeInsetsSupport
{
const UIEdgeInsets fromEdgeInsets = UIEdgeInsetsZero;
const UIEdgeInsets toEdgeInsets = UIEdgeInsetsMake(100, 200, 200, 400);
const UIEdgeInsets velocityEdgeInsets = UIEdgeInsetsMake(1000, 1000, 1000, 1000);
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPScrollViewContentInset];
anim.fromValue = [NSValue valueWithUIEdgeInsets:fromEdgeInsets];
anim.toValue = [NSValue valueWithUIEdgeInsets:toEdgeInsets];
anim.velocity = [NSValue valueWithUIEdgeInsets:velocityEdgeInsets];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// expect start and stop to be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
id scrollView = [OCMockObject niceMockForClass:[UIScrollView class]];
[scrollView pop_addAnimation:anim forKey:nil];
// expect final value to be set
[[scrollView expect] setContentInset:toEdgeInsets];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify delegate
[delegate verify];
// verify scroll view
[scrollView verify];
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
UIEdgeInsets lastEdgeInsets = [lastEvent.value UIEdgeInsetsValue];
// verify last insets are to insets
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(lastEdgeInsets, toEdgeInsets), @"unexpected last edge insets value: %@", lastEvent);
}
#endif
- (void)testColorSupport
{
CGFloat fromValues[4] = {1, 1, 1, 1};
CGFloat toValues[4] = {0, 0, 0, 1};
CGColorRef fromColor = POPCGColorRGBACreate(fromValues);
CGColorRef toColor = POPCGColorRGBACreate(toValues);
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBounds];
anim.fromValue = (__bridge_transfer id)fromColor;
anim.toValue = (__bridge_transfer id)toColor;
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
// run animation
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
// verify delegate
[delegate verify];
// expect some interpolation
XCTAssertTrue(writeEvents.count > 1, @"unexpected write events %@", writeEvents);
POPAnimationValueEvent *lastEvent = [writeEvents lastObject];
// verify last written color is to color
POPAssertColorEqual((__bridge CGColorRef)lastEvent.value, (__bridge CGColorRef)anim.toValue);
}
static BOOL _floatingPointEqual(CGFloat a, CGFloat b)
{
CGFloat epsilon = 0.0001;
return std::abs(a - b) < epsilon;
}
- (void)testBouncinessSpeedToTensionFrictionConversion
{
CGFloat sampleBounciness = 12.0;
CGFloat sampleSpeed = 5.0;
CGFloat tension, friction, mass;
[POPSpringAnimation convertBounciness:sampleBounciness speed:sampleSpeed toTension:&tension friction:&friction mass:&mass];
CGFloat outBounciness, outSpeed;
[POPSpringAnimation convertTension:tension friction:friction toBounciness:&outBounciness speed:&outSpeed];
XCTAssertTrue(_floatingPointEqual(sampleBounciness, outBounciness) && _floatingPointEqual(sampleSpeed, outSpeed), @"(bounciness, speed) conversion failed. Mapped (%f, %f) back to (%f, %f)", sampleBounciness, sampleSpeed, outBounciness, outSpeed);
}
- (void)testTensionFrictionToBouncinessSpeedConversion
{
CGFloat sampleTension = 240.0;
CGFloat sampleFriction = 25.0;
CGFloat bounciness, speed;
[POPSpringAnimation convertTension:sampleTension friction:sampleFriction toBounciness:&bounciness speed:&speed];
CGFloat outTension, outFriction, outMass;
[POPSpringAnimation convertBounciness:bounciness speed:speed toTension:&outTension friction:&outFriction mass:&outMass];
XCTAssertTrue(_floatingPointEqual(sampleTension, outTension) && _floatingPointEqual(sampleFriction, outFriction), @"(tension, friction) conversion failed. Mapped (%f, %f) back to (%f, %f)", sampleTension, sampleFriction, outTension, outFriction);
}
- (void)testRemovedOnCompletionNoContinuationValues
{
static CGFloat fromValue = 400.0;
static NSArray *toValues = @[@200.0, @400.0];
// configure animation
POPSpringAnimation *anim = self._positionAnimation;
anim.fromValue = [NSNumber numberWithFloat:fromValue];
anim.toValue = toValues[0];
anim.removedOnCompletion = NO;
// run animation, from 400 to 200
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// assert reached to value
XCTAssertTrue(layer.position.x == [anim.toValue floatValue], @"unexpected value:%@ %@", layer, anim);
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// update to value, animate from 200 to 400
anim.toValue = toValues[1];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// verify from 200 to 400
NSArray *writeEvents = [tracer eventsWithType:kPOPAnimationEventPropertyWrite];
XCTAssertTrue(writeEvents.count > 5, @"unexpected frame count %@", writeEvents);
CGFloat firstValue = [[(POPAnimationValueEvent *)[writeEvents firstObject] value] floatValue];
CGFloat lastValue = [[(POPAnimationValueEvent *)[writeEvents lastObject] value] floatValue];
XCTAssertEqualWithAccuracy(((CGFloat)[toValues[0] floatValue]), firstValue, 10, @"unexpected first value %@", writeEvents);
XCTAssertEqualWithAccuracy(((CGFloat)[toValues[1] floatValue]), lastValue, 10, @"unexpected last value %@", writeEvents);
}
- (void)testNilColor
{
POPSpringAnimation *anim = [POPSpringAnimation animation];
anim.property = [POPAnimatableProperty propertyWithName:kPOPLayerBackgroundColor];
#if TARGET_OS_IPHONE
anim.toValue = (__bridge id)[UIColor redColor].CGColor;
#else
anim.toValue = (__bridge id)[NSColor redColor].CGColor;
#endif
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// run animation
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// verify valid from color exists
CGColorRef fromColor = (__bridge CGColorRef)anim.fromValue;
XCTAssertTrue(fromColor, @"unexpected value %p", fromColor);
// verify from color clear
#if TARGET_OS_IPHONE
POPAssertColorEqual(fromColor, [UIColor clearColor].CGColor);
#else
POPAssertColorEqual(fromColor, [NSColor clearColor].CGColor);
#endif
}
- (void)testExcessiveJumpInTime
{
POPSpringAnimation *anim = self._positionAnimation;
anim.toValue = @(1000.0);
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
id delegate = [OCMockObject niceMockForProtocol:@protocol(POPAnimationDelegate)];
anim.delegate = delegate;
// expect start and stop to be called
[[delegate expect] pop_animationDidStart:anim];
[[delegate expect] pop_animationDidStop:anim finished:YES];
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:animationKey];
// render with large time jump
POPAnimatorRenderTimes(self.animator, self.beginTime, @[@0.0, @0.01, @300]);
// verify start stop
[delegate verify];
// verify last write event value
POPAnimationValueEvent *writeEvent = [[tracer eventsWithType:kPOPAnimationEventPropertyWrite] lastObject];
XCTAssertEqualObjects(writeEvent.value, anim.toValue, @"unexpected last write event %@", writeEvent);
}
- (void)testEquivalentFromToValues
{
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition];
anim.fromValue = [NSValue valueWithCGPoint:CGPointZero];
anim.toValue = [NSValue valueWithCGPoint:CGPointZero];
anim.velocity = [NSValue valueWithCGPoint:CGPointMake(1000.0, 1000.0)];
// start tracer
POPAnimationTracer *tracer = anim.tracer;
[tracer start];
// run animation
CALayer *layer = [CALayer layer];
[layer pop_addAnimation:anim forKey:@""];
POPAnimatorRenderDuration(self.animator, self.beginTime, 3, 1.0/60.0);
// verify last write event value
POPAnimationValueEvent *writeEvent = [[tracer eventsWithType:kPOPAnimationEventPropertyWrite] lastObject];
XCTAssertEqualObjects(writeEvent.value, anim.toValue, @"unexpected last write event %@", writeEvent);
}
- (void)testNSCopyingSupportPOPSpringAnimation
{
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:@"asdf_asdf_asdf"];
configureConcretePropertyAnimation(anim);
anim.velocity = @(4321);
anim.springBounciness = 11.1;
anim.springSpeed = 12;
anim.dynamicsTension = 0.83;
anim.dynamicsFriction = 0.97;
anim.dynamicsMass = 100;
POPSpringAnimation *copy = [anim copy];
XCTAssertEqualObjects(copy.velocity, anim.velocity, @"expected equality; value1:%@ value2:%@", copy.velocity, anim.velocity);
XCTAssertEqual(copy.springBounciness, anim.springBounciness, @"expected equality; value1:%@ value2:%@", @(copy.springBounciness), @(anim.springBounciness));
XCTAssertEqual(copy.springSpeed, anim.springSpeed, @"expected equality; value1:%@ value2:%@", @(copy.springSpeed), @(anim.springSpeed));
XCTAssertEqual(copy.dynamicsTension, anim.dynamicsTension, @"expected equality; value1:%@ value2:%@", @(copy.dynamicsTension), @(anim.dynamicsTension));
XCTAssertEqual(copy.dynamicsFriction, anim.dynamicsFriction, @"expected equality; value1:%@ value2:%@", @(copy.dynamicsFriction), @(anim.dynamicsFriction));
XCTAssertEqual(copy.dynamicsMass, anim.dynamicsMass, @"expected equality; value1:%@ value2:%@", @(copy.dynamicsMass), @(anim.dynamicsMass));
}
@end