diff --git a/examples/apps/Catalog/MotionAnimatorCatalog.xcodeproj/project.pbxproj b/examples/apps/Catalog/MotionAnimatorCatalog.xcodeproj/project.pbxproj index 61fadc1..943ff68 100644 --- a/examples/apps/Catalog/MotionAnimatorCatalog.xcodeproj/project.pbxproj +++ b/examples/apps/Catalog/MotionAnimatorCatalog.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 660636021FACC24300C3DFB8 /* TimeScaleFactorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660636011FACC24300C3DFB8 /* TimeScaleFactorTests.swift */; }; 6625876C1FB4DB9C00BC7DF1 /* InitialVelocityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6625876B1FB4DB9C00BC7DF1 /* InitialVelocityTests.swift */; }; 664F59941FCCE27E002EC56D /* UIKitBehavioralTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664F59931FCCE27E002EC56D /* UIKitBehavioralTests.swift */; }; + 664F59961FCDB2E6002EC56D /* QuartzCoreBehavioralTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664F59951FCDB2E5002EC56D /* QuartzCoreBehavioralTests.swift */; }; 666FAA841D384A6B000363DA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 666FAA831D384A6B000363DA /* AppDelegate.swift */; }; 666FAA8B1D384A6B000363DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 666FAA8A1D384A6B000363DA /* Assets.xcassets */; }; 666FAA8E1D384A6B000363DA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 666FAA8C1D384A6B000363DA /* LaunchScreen.storyboard */; }; @@ -53,6 +54,7 @@ 660636011FACC24300C3DFB8 /* TimeScaleFactorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeScaleFactorTests.swift; sourceTree = ""; }; 6625876B1FB4DB9C00BC7DF1 /* InitialVelocityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialVelocityTests.swift; sourceTree = ""; }; 664F59931FCCE27E002EC56D /* UIKitBehavioralTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBehavioralTests.swift; sourceTree = ""; }; + 664F59951FCDB2E5002EC56D /* QuartzCoreBehavioralTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuartzCoreBehavioralTests.swift; sourceTree = ""; }; 666FAA801D384A6B000363DA /* MotionAnimatorCatalog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MotionAnimatorCatalog.app; sourceTree = BUILT_PRODUCTS_DIR; }; 666FAA831D384A6B000363DA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Catalog/AppDelegate.swift; sourceTree = ""; }; 666FAA8A1D384A6B000363DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -223,6 +225,7 @@ 66EF6F291FC48D6A00C83A63 /* InstantAnimationTests.swift */, 66FD99F91EE9FBBE00C53A82 /* MotionAnimatorTests.m */, 668726491EF04B4C00113675 /* MotionAnimatorTests.swift */, + 664F59951FCDB2E5002EC56D /* QuartzCoreBehavioralTests.swift */, 660636011FACC24300C3DFB8 /* TimeScaleFactorTests.swift */, 664F59931FCCE27E002EC56D /* UIKitBehavioralTests.swift */, ); @@ -506,6 +509,7 @@ 66EF6F2A1FC48D6A00C83A63 /* InstantAnimationTests.swift in Sources */, 660636021FACC24300C3DFB8 /* TimeScaleFactorTests.swift in Sources */, 66BF5A8F1FB0E4CB00E864F6 /* ImplicitAnimationTests.swift in Sources */, + 664F59961FCDB2E6002EC56D /* QuartzCoreBehavioralTests.swift in Sources */, 66A6A6681FBA158000DE54CB /* AnimationRemovalTests.swift in Sources */, 6687264A1EF04B4C00113675 /* MotionAnimatorTests.swift in Sources */, 66FD99FA1EE9FBBE00C53A82 /* MotionAnimatorTests.m in Sources */, diff --git a/tests/unit/QuartzCoreBehavioralTests.swift b/tests/unit/QuartzCoreBehavioralTests.swift new file mode 100644 index 0000000..8534efe --- /dev/null +++ b/tests/unit/QuartzCoreBehavioralTests.swift @@ -0,0 +1,121 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import XCTest +#if IS_BAZEL_BUILD +import _MotionAnimator +#else +import MotionAnimator +#endif + +class QuartzCoreBehavioralTests: XCTestCase { + var layer: CAShapeLayer! + + var originalImplementation: IMP? + override func setUp() { + super.setUp() + + let window = UIWindow() + window.makeKeyAndVisible() + layer = CAShapeLayer() + window.layer.addSublayer(layer) + + rebuildLayer() + } + + override func tearDown() { + layer = nil + + super.tearDown() + } + + private func rebuildLayer() { + let oldSuperlayer = layer.superlayer! + layer.removeFromSuperlayer() + layer = CAShapeLayer() + oldSuperlayer.addSublayer(layer) + + // Connect our layers to the render server. + CATransaction.flush() + } + + func testWhichPropertiesImplicitlyAnimateAdditively() { + let properties: [AnimatableKeyPath: Any] = [:] + for (keyPath, value) in properties { + rebuildLayer() + + layer.setValue(value, forKeyPath: keyPath.rawValue) + + XCTAssertNotNil(layer.animationKeys(), + "Expected \(keyPath.rawValue) to generate at least one animation.") + if let animationKeys = layer.animationKeys() { + for key in animationKeys { + let animation = layer.animation(forKey: key) as! CABasicAnimation + XCTAssertTrue(animation.isAdditive, + "Expected \(key) to be additive as a result of animating " + + "\(keyPath.rawValue), but it was not: \(animation.debugDescription).") + } + } + } + } + + func testWhichPropertiesImplicitlyAnimateButNotAdditively() { + let properties: [AnimatableKeyPath: Any] = [ + .cornerRadius: 3, + .height: 100, + .opacity: 0.5, + .position: CGPoint(x: 50, y: 20), + .rotation: 42, + .scale: 2.5, + .strokeStart: 0.2, + .strokeEnd: 0.5, + .width: 25, + .x: 12, + .y: 23, + ] + for (keyPath, value) in properties { + rebuildLayer() + + layer.setValue(value, forKeyPath: keyPath.rawValue) + + XCTAssertNotNil(layer.animationKeys(), + "Expected \(keyPath.rawValue) to generate at least one animation.") + if let animationKeys = layer.animationKeys() { + for key in animationKeys { + let animation = layer.animation(forKey: key) as! CABasicAnimation + XCTAssertFalse(animation.isAdditive, + "Expected \(key) not to be additive as a result of animating " + + "\(keyPath.rawValue), but it was: \(animation.debugDescription).") + } + } + } + } + + func testWhichPropertiesDoNotImplicitlyAnimate() { + let properties: [AnimatableKeyPath: Any] = [ + .backgroundColor: UIColor.blue, + ] + for (keyPath, value) in properties { + rebuildLayer() + + layer.setValue(value, forKeyPath: keyPath.rawValue) + + XCTAssertNil(layer.animationKeys(), + "Expected \(keyPath.rawValue) not to generate any animations.") + } + } +} +