second_curtain 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +20 -0
- data/.travis.yml +7 -0
- data/CHANGELOG +20 -0
- data/Demo/Demo/ASHAppDelegate.h +15 -0
- data/Demo/Demo/ASHAppDelegate.m +18 -0
- data/Demo/Demo/ASHViewController.h +13 -0
- data/Demo/Demo/ASHViewController.m +30 -0
- data/Demo/Demo/Base.lproj/Main.storyboard +30 -0
- data/Demo/Demo/Demo-Info.plist +40 -0
- data/Demo/Demo/Demo-Prefix.pch +16 -0
- data/Demo/Demo/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
- data/Demo/Demo/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
- data/Demo/Demo/en.lproj/InfoPlist.strings +2 -0
- data/Demo/Demo/main.m +18 -0
- data/Demo/Demo.xcodeproj/project.pbxproj +517 -0
- data/Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- data/Demo/Demo.xcodeproj/project.xcworkspace/xcshareddata/Demo.xccheckout +41 -0
- data/Demo/Demo.xcodeproj/project.xcworkspace/xcuserdata/ash.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- data/Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme +96 -0
- data/Demo/Demo.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
- data/Demo/Demo.xcodeproj/xcuserdata/orta.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
- data/Demo/Demo.xcworkspace/contents.xcworkspacedata +1 -0
- data/Demo/Demo.xcworkspace/xcshareddata/Demo.xccheckout +41 -0
- data/Demo/Demo.xcworkspace/xcuserdata/ash.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- data/Demo/Demo.xcworkspace/xcuserdata/orta.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- data/Demo/DemoTests/DemoTests-Info.plist +22 -0
- data/Demo/DemoTests/DemoTests.m +52 -0
- data/Demo/DemoTests/ReferenceImages/ASHViewControllerSpec/a_view_controller_with_a_loaded_view_should_have_a_valid_snapshot@2x.png +0 -0
- data/Demo/DemoTests/ReferenceImages/ASHViewControllerSpec/views_should_be_green@2x.png +0 -0
- data/Demo/DemoTests/ReferenceImages/ASHViewControllerSpec/views_should_be_red@2x.png +0 -0
- data/Demo/DemoTests/en.lproj/InfoPlist.strings +2 -0
- data/Demo/Makefile +15 -0
- data/Demo/Podfile +16 -0
- data/Demo/Podfile.lock +21 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPBackwardCompatibility.h +42 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPBlockDefinedMatcher.h +25 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPDefines.h +17 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPDoubleTuple.h +13 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPExpect.h +45 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPFloatTuple.h +13 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatcher.h +20 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatcherHelpers.h +4 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beCloseTo.h +7 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beFalsy.h +3 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beGreaterThan.h +6 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beGreaterThanOrEqualTo.h +6 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beIdenticalTo.h +10 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beInTheRangeOf.h +6 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beInstanceOf.h +7 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beKindOf.h +5 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beLessThan.h +6 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beLessThanOrEqualTo.h +6 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beNil.h +5 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beSubclassOf.h +5 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beSupersetOf.h +4 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beTruthy.h +3 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beginWith.h +5 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+conformTo.h +3 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+contain.h +5 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+endWith.h +3 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+equal.h +5 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+haveCountOf.h +10 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+notify.h +4 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+raise.h +4 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+raiseWithReason.h +3 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+respondTo.h +3 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers.h +24 -0
- data/Demo/Pods/BuildHeaders/Expecta/EXPUnsupportedObject.h +11 -0
- data/Demo/Pods/BuildHeaders/Expecta/Expecta.h +27 -0
- data/Demo/Pods/BuildHeaders/Expecta/ExpectaSupport.h +64 -0
- data/Demo/Pods/BuildHeaders/Expecta/NSObject+Expecta.h +14 -0
- data/Demo/Pods/BuildHeaders/Expecta/NSValue+Expecta.h +8 -0
- data/Demo/Pods/BuildHeaders/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h +21 -0
- data/Demo/Pods/BuildHeaders/FBSnapshotTestCase/FBSnapshotTestCase.h +87 -0
- data/Demo/Pods/BuildHeaders/FBSnapshotTestCase/FBSnapshotTestController.h +147 -0
- data/Demo/Pods/BuildHeaders/FBSnapshotTestCase/UIImage+Compare.h +37 -0
- data/Demo/Pods/BuildHeaders/FBSnapshotTestCase/UIImage+Diff.h +37 -0
- data/Demo/Pods/BuildHeaders/Specta/SPTExample.h +14 -0
- data/Demo/Pods/BuildHeaders/Specta/SPTExampleGroup.h +40 -0
- data/Demo/Pods/BuildHeaders/Specta/SPTNestedReporter.h +5 -0
- data/Demo/Pods/BuildHeaders/Specta/SPTReporter.h +33 -0
- data/Demo/Pods/BuildHeaders/Specta/SPTSharedExampleGroups.h +17 -0
- data/Demo/Pods/BuildHeaders/Specta/SPTSpec.h +21 -0
- data/Demo/Pods/BuildHeaders/Specta/SPTXCTestCase.h +27 -0
- data/Demo/Pods/BuildHeaders/Specta/SPTXCTestReporter.h +9 -0
- data/Demo/Pods/BuildHeaders/Specta/Specta.h +70 -0
- data/Demo/Pods/BuildHeaders/Specta/SpectaSupport.h +46 -0
- data/Demo/Pods/BuildHeaders/Specta/SpectaTypes.h +3 -0
- data/Demo/Pods/BuildHeaders/Specta/SpectaUtility.h +14 -0
- data/Demo/Pods/BuildHeaders/Specta/XCTestCase+Specta.h +4 -0
- data/Demo/Pods/BuildHeaders/Specta/XCTestLog+Specta.h +7 -0
- data/Demo/Pods/BuildHeaders/Specta/XCTestRun+Specta.h +8 -0
- data/Demo/Pods/Expecta/LICENSE +19 -0
- data/Demo/Pods/Expecta/README.md +308 -0
- data/Demo/Pods/Expecta/src/EXPBackwardCompatibility.h +42 -0
- data/Demo/Pods/Expecta/src/EXPBackwardCompatibility.m +17 -0
- data/Demo/Pods/Expecta/src/EXPBlockDefinedMatcher.h +25 -0
- data/Demo/Pods/Expecta/src/EXPBlockDefinedMatcher.m +60 -0
- data/Demo/Pods/Expecta/src/EXPDefines.h +17 -0
- data/Demo/Pods/Expecta/src/EXPDoubleTuple.h +13 -0
- data/Demo/Pods/Expecta/src/EXPDoubleTuple.m +42 -0
- data/Demo/Pods/Expecta/src/EXPExpect.h +45 -0
- data/Demo/Pods/Expecta/src/EXPExpect.m +214 -0
- data/Demo/Pods/Expecta/src/EXPFloatTuple.h +13 -0
- data/Demo/Pods/Expecta/src/EXPFloatTuple.m +42 -0
- data/Demo/Pods/Expecta/src/EXPMatcher.h +20 -0
- data/Demo/Pods/Expecta/src/EXPUnsupportedObject.h +11 -0
- data/Demo/Pods/Expecta/src/EXPUnsupportedObject.m +20 -0
- data/Demo/Pods/Expecta/src/Expecta.h +27 -0
- data/Demo/Pods/Expecta/src/Expecta.m +15 -0
- data/Demo/Pods/Expecta/src/ExpectaSupport.h +64 -0
- data/Demo/Pods/Expecta/src/ExpectaSupport.m +187 -0
- data/Demo/Pods/Expecta/src/NSObject+Expecta.h +14 -0
- data/Demo/Pods/Expecta/src/NSValue+Expecta.h +8 -0
- data/Demo/Pods/Expecta/src/NSValue+Expecta.m +21 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatcherHelpers.h +4 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatcherHelpers.m +9 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beCloseTo.h +7 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beCloseTo.m +49 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beFalsy.h +3 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beFalsy.m +24 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beGreaterThan.h +6 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beGreaterThan.m +20 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beGreaterThanOrEqualTo.h +6 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beGreaterThanOrEqualTo.m +20 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beIdenticalTo.h +10 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beIdenticalTo.m +24 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beInTheRangeOf.h +6 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beInTheRangeOf.m +30 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beInstanceOf.h +7 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beInstanceOf.m +27 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beKindOf.h +5 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beKindOf.m +27 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beLessThan.h +6 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beLessThan.m +20 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beLessThanOrEqualTo.h +6 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beLessThanOrEqualTo.m +20 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beNil.h +5 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beNil.m +16 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beSubclassOf.h +5 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beSubclassOf.m +27 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beSupersetOf.h +4 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beSupersetOf.m +57 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beTruthy.h +3 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beTruthy.m +24 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beginWith.h +5 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beginWith.m +49 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+conformTo.h +3 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+conformTo.m +33 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+contain.h +5 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+contain.m +38 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+endWith.h +3 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+endWith.m +49 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+equal.h +5 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+equal.m +24 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+haveCountOf.h +10 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+haveCountOf.m +36 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+notify.h +4 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+notify.m +63 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+raise.h +4 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+raise.m +30 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+raiseWithReason.h +3 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+raiseWithReason.m +35 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+respondTo.h +3 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers+respondTo.m +28 -0
- data/Demo/Pods/Expecta/src/matchers/EXPMatchers.h +24 -0
- data/Demo/Pods/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h +21 -0
- data/Demo/Pods/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.m +308 -0
- data/Demo/Pods/Expecta+Snapshots/LICENSE.md +22 -0
- data/Demo/Pods/Expecta+Snapshots/README.md +85 -0
- data/Demo/Pods/FBSnapshotTestCase/FBSnapshotTestCase.h +87 -0
- data/Demo/Pods/FBSnapshotTestCase/FBSnapshotTestCase.m +82 -0
- data/Demo/Pods/FBSnapshotTestCase/FBSnapshotTestController.h +147 -0
- data/Demo/Pods/FBSnapshotTestCase/FBSnapshotTestController.m +376 -0
- data/Demo/Pods/FBSnapshotTestCase/LICENSE +29 -0
- data/Demo/Pods/FBSnapshotTestCase/README.md +83 -0
- data/Demo/Pods/FBSnapshotTestCase/UIImage+Compare.h +37 -0
- data/Demo/Pods/FBSnapshotTestCase/UIImage+Compare.m +87 -0
- data/Demo/Pods/FBSnapshotTestCase/UIImage+Diff.h +37 -0
- data/Demo/Pods/FBSnapshotTestCase/UIImage+Diff.m +56 -0
- data/Demo/Pods/Headers/Expecta/EXPBackwardCompatibility.h +42 -0
- data/Demo/Pods/Headers/Expecta/EXPBlockDefinedMatcher.h +25 -0
- data/Demo/Pods/Headers/Expecta/EXPDefines.h +17 -0
- data/Demo/Pods/Headers/Expecta/EXPDoubleTuple.h +13 -0
- data/Demo/Pods/Headers/Expecta/EXPExpect.h +45 -0
- data/Demo/Pods/Headers/Expecta/EXPFloatTuple.h +13 -0
- data/Demo/Pods/Headers/Expecta/EXPMatcher.h +20 -0
- data/Demo/Pods/Headers/Expecta/EXPMatcherHelpers.h +4 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beCloseTo.h +7 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beFalsy.h +3 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beGreaterThan.h +6 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beGreaterThanOrEqualTo.h +6 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beIdenticalTo.h +10 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beInTheRangeOf.h +6 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beInstanceOf.h +7 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beKindOf.h +5 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beLessThan.h +6 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beLessThanOrEqualTo.h +6 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beNil.h +5 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beSubclassOf.h +5 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beSupersetOf.h +4 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beTruthy.h +3 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+beginWith.h +5 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+conformTo.h +3 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+contain.h +5 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+endWith.h +3 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+equal.h +5 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+haveCountOf.h +10 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+notify.h +4 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+raise.h +4 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+raiseWithReason.h +3 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers+respondTo.h +3 -0
- data/Demo/Pods/Headers/Expecta/EXPMatchers.h +24 -0
- data/Demo/Pods/Headers/Expecta/EXPUnsupportedObject.h +11 -0
- data/Demo/Pods/Headers/Expecta/Expecta.h +27 -0
- data/Demo/Pods/Headers/Expecta/ExpectaSupport.h +64 -0
- data/Demo/Pods/Headers/Expecta/NSObject+Expecta.h +14 -0
- data/Demo/Pods/Headers/Expecta/NSValue+Expecta.h +8 -0
- data/Demo/Pods/Headers/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h +21 -0
- data/Demo/Pods/Headers/FBSnapshotTestCase/FBSnapshotTestCase.h +87 -0
- data/Demo/Pods/Headers/FBSnapshotTestCase/FBSnapshotTestController.h +147 -0
- data/Demo/Pods/Headers/FBSnapshotTestCase/UIImage+Compare.h +37 -0
- data/Demo/Pods/Headers/FBSnapshotTestCase/UIImage+Diff.h +37 -0
- data/Demo/Pods/Headers/Specta/SPTExample.h +14 -0
- data/Demo/Pods/Headers/Specta/SPTExampleGroup.h +40 -0
- data/Demo/Pods/Headers/Specta/SPTNestedReporter.h +5 -0
- data/Demo/Pods/Headers/Specta/SPTReporter.h +33 -0
- data/Demo/Pods/Headers/Specta/SPTSharedExampleGroups.h +17 -0
- data/Demo/Pods/Headers/Specta/SPTSpec.h +21 -0
- data/Demo/Pods/Headers/Specta/SPTXCTestCase.h +27 -0
- data/Demo/Pods/Headers/Specta/SPTXCTestReporter.h +9 -0
- data/Demo/Pods/Headers/Specta/Specta.h +70 -0
- data/Demo/Pods/Headers/Specta/SpectaSupport.h +46 -0
- data/Demo/Pods/Headers/Specta/SpectaTypes.h +3 -0
- data/Demo/Pods/Headers/Specta/SpectaUtility.h +14 -0
- data/Demo/Pods/Headers/Specta/XCTestCase+Specta.h +4 -0
- data/Demo/Pods/Headers/Specta/XCTestLog+Specta.h +7 -0
- data/Demo/Pods/Headers/Specta/XCTestRun+Specta.h +8 -0
- data/Demo/Pods/Manifest.lock +21 -0
- data/Demo/Pods/Pods-DemoTests-EXPMatchers+FBSnapshotTest-Private.xcconfig +6 -0
- data/Demo/Pods/Pods-DemoTests-EXPMatchers+FBSnapshotTest-dummy.m +5 -0
- data/Demo/Pods/Pods-DemoTests-EXPMatchers+FBSnapshotTest-prefix.pch +5 -0
- data/Demo/Pods/Pods-DemoTests-EXPMatchers+FBSnapshotTest.xcconfig +2 -0
- data/Demo/Pods/Pods-DemoTests-Expecta+Snapshots-Private.xcconfig +6 -0
- data/Demo/Pods/Pods-DemoTests-Expecta+Snapshots-dummy.m +5 -0
- data/Demo/Pods/Pods-DemoTests-Expecta+Snapshots-prefix.pch +5 -0
- data/Demo/Pods/Pods-DemoTests-Expecta+Snapshots.xcconfig +2 -0
- data/Demo/Pods/Pods-DemoTests-Expecta-Private.xcconfig +5 -0
- data/Demo/Pods/Pods-DemoTests-Expecta-dummy.m +5 -0
- data/Demo/Pods/Pods-DemoTests-Expecta-prefix.pch +5 -0
- data/Demo/Pods/Pods-DemoTests-Expecta.xcconfig +1 -0
- data/Demo/Pods/Pods-DemoTests-FBSnapshotTestCase-Private.xcconfig +6 -0
- data/Demo/Pods/Pods-DemoTests-FBSnapshotTestCase-dummy.m +5 -0
- data/Demo/Pods/Pods-DemoTests-FBSnapshotTestCase-prefix.pch +6 -0
- data/Demo/Pods/Pods-DemoTests-FBSnapshotTestCase.xcconfig +2 -0
- data/Demo/Pods/Pods-DemoTests-Specta-Private.xcconfig +6 -0
- data/Demo/Pods/Pods-DemoTests-Specta-dummy.m +5 -0
- data/Demo/Pods/Pods-DemoTests-Specta-prefix.pch +5 -0
- data/Demo/Pods/Pods-DemoTests-Specta.xcconfig +2 -0
- data/Demo/Pods/Pods-DemoTests-acknowledgements.markdown +109 -0
- data/Demo/Pods/Pods-DemoTests-acknowledgements.plist +151 -0
- data/Demo/Pods/Pods-DemoTests-dummy.m +5 -0
- data/Demo/Pods/Pods-DemoTests-environment.h +32 -0
- data/Demo/Pods/Pods-DemoTests-resources.sh +68 -0
- data/Demo/Pods/Pods-DemoTests.xcconfig +6 -0
- data/Demo/Pods/Pods.xcodeproj/project.pbxproj +1390 -0
- data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/Pods-DemoTests-Expecta+Snapshots.xcscheme +59 -0
- data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/Pods-DemoTests-Expecta.xcscheme +59 -0
- data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/Pods-DemoTests-FBSnapshotTestCase.xcscheme +59 -0
- data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/Pods-DemoTests-Specta.xcscheme +59 -0
- data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/Pods-DemoTests.xcscheme +59 -0
- data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/xcschememanagement.plist +62 -0
- data/Demo/Pods/Specta/LICENSE +20 -0
- data/Demo/Pods/Specta/README.md +146 -0
- data/Demo/Pods/Specta/src/SPTExample.h +14 -0
- data/Demo/Pods/Specta/src/SPTExample.m +16 -0
- data/Demo/Pods/Specta/src/SPTExampleGroup.h +40 -0
- data/Demo/Pods/Specta/src/SPTExampleGroup.m +326 -0
- data/Demo/Pods/Specta/src/SPTNestedReporter.h +5 -0
- data/Demo/Pods/Specta/src/SPTNestedReporter.m +194 -0
- data/Demo/Pods/Specta/src/SPTReporter.h +33 -0
- data/Demo/Pods/Specta/src/SPTReporter.m +134 -0
- data/Demo/Pods/Specta/src/SPTSharedExampleGroups.h +17 -0
- data/Demo/Pods/Specta/src/SPTSharedExampleGroups.m +62 -0
- data/Demo/Pods/Specta/src/SPTSpec.h +21 -0
- data/Demo/Pods/Specta/src/SPTSpec.m +31 -0
- data/Demo/Pods/Specta/src/SPTXCTestCase.h +27 -0
- data/Demo/Pods/Specta/src/SPTXCTestCase.m +151 -0
- data/Demo/Pods/Specta/src/SPTXCTestReporter.h +9 -0
- data/Demo/Pods/Specta/src/SPTXCTestReporter.m +11 -0
- data/Demo/Pods/Specta/src/Specta.h +70 -0
- data/Demo/Pods/Specta/src/Specta.m +144 -0
- data/Demo/Pods/Specta/src/SpectaSupport.h +46 -0
- data/Demo/Pods/Specta/src/SpectaTypes.h +3 -0
- data/Demo/Pods/Specta/src/SpectaUtility.h +14 -0
- data/Demo/Pods/Specta/src/SpectaUtility.m +65 -0
- data/Demo/Pods/Specta/src/XCTestCase+Specta.h +4 -0
- data/Demo/Pods/Specta/src/XCTestCase+Specta.m +26 -0
- data/Demo/Pods/Specta/src/XCTestLog+Specta.h +7 -0
- data/Demo/Pods/Specta/src/XCTestLog+Specta.m +49 -0
- data/Demo/Pods/Specta/src/XCTestRun+Specta.h +8 -0
- data/Demo/Pods/Specta/src/XCTestRun+Specta.m +44 -0
- data/Gemfile +5 -0
- data/LICENSE +21 -0
- data/README.md +146 -0
- data/Rakefile +7 -0
- data/second_curtain.gemspec +20 -0
- data/spec/sample_output.txt +79 -0
- data/spec/second_shutter/kaleidoscope_command_spec.rb +25 -0
- data/spec/second_shutter/parser_spec.rb +39 -0
- data/spec/second_shutter/test_suite_spec.rb +32 -0
- data/spec/second_shutter/upload_manager_spec.rb +60 -0
- data/spec/second_shutter/upload_spec.rb +34 -0
- data/spec/second_shutter/xcode_test_case_spec.rb +22 -0
- metadata +315 -1
@@ -0,0 +1,147 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2013, Facebook, Inc.
|
3
|
+
* All rights reserved.
|
4
|
+
*
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
6
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
7
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
8
|
+
*
|
9
|
+
*/
|
10
|
+
|
11
|
+
#import <Foundation/Foundation.h>
|
12
|
+
#import <UIKit/UIKit.h>
|
13
|
+
|
14
|
+
typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) {
|
15
|
+
FBSnapshotTestControllerErrorCodeUnknown,
|
16
|
+
FBSnapshotTestControllerErrorCodeNeedsRecord,
|
17
|
+
FBSnapshotTestControllerErrorCodePNGCreationFailed,
|
18
|
+
FBSnapshotTestControllerErrorCodeImagesDifferentSizes,
|
19
|
+
FBSnapshotTestControllerErrorCodeImagesDifferent,
|
20
|
+
};
|
21
|
+
/**
|
22
|
+
Errors returned by the methods of FBSnapshotTestController use this domain.
|
23
|
+
*/
|
24
|
+
extern NSString *const FBSnapshotTestControllerErrorDomain;
|
25
|
+
|
26
|
+
/**
|
27
|
+
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
|
28
|
+
*/
|
29
|
+
extern NSString *const FBReferenceImageFilePathKey;
|
30
|
+
|
31
|
+
/**
|
32
|
+
Provides the heavy-lifting for FBSnapshotTestCase. It loads and saves images, along with performing the actual pixel-
|
33
|
+
by-pixel comparison of images.
|
34
|
+
Instances are initialized with the test class, and directories to read and write to.
|
35
|
+
*/
|
36
|
+
@interface FBSnapshotTestController : NSObject
|
37
|
+
|
38
|
+
/**
|
39
|
+
Record snapshots.
|
40
|
+
**/
|
41
|
+
@property(readwrite, nonatomic, assign) BOOL recordMode;
|
42
|
+
|
43
|
+
/**
|
44
|
+
Designated initializer.
|
45
|
+
Before this methods returns the controller enumerates over the test methods in `testClass` and loads the images
|
46
|
+
for those tests.
|
47
|
+
@param testClass The subclass of FBSnapshotTestCase that is using this controller.
|
48
|
+
@param referenceImagesDirectory The directory where the reference images are stored.
|
49
|
+
@returns An instance of FBSnapshotTestController.
|
50
|
+
*/
|
51
|
+
- (id)initWithTestClass:(Class)testClass;
|
52
|
+
|
53
|
+
|
54
|
+
/**
|
55
|
+
Performs the comparison of the layer.
|
56
|
+
@param layer The Layer to snapshot.
|
57
|
+
@param referenceImagesDirectory The directory in which reference images are stored.
|
58
|
+
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
59
|
+
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
60
|
+
@returns YES if the comparison (or saving of the reference image) succeeded.
|
61
|
+
*/
|
62
|
+
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
63
|
+
selector:(SEL)selector
|
64
|
+
identifier:(NSString *)identifier
|
65
|
+
error:(NSError **)errorPtr;
|
66
|
+
|
67
|
+
/**
|
68
|
+
Performs the comparison of the view.
|
69
|
+
@param view The view to snapshot.
|
70
|
+
@param referenceImagesDirectory The directory in which reference images are stored.
|
71
|
+
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
72
|
+
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
73
|
+
@returns YES if the comparison (or saving of the reference image) succeeded.
|
74
|
+
*/
|
75
|
+
- (BOOL)compareSnapshotOfView:(UIView *)view
|
76
|
+
selector:(SEL)selector
|
77
|
+
identifier:(NSString *)identifier
|
78
|
+
error:(NSError **)errorPtr;
|
79
|
+
|
80
|
+
/**
|
81
|
+
Performs the comparison of a view or layer.
|
82
|
+
@param view The view or layer to snapshot.
|
83
|
+
@param referenceImagesDirectory The directory in which reference images are stored.
|
84
|
+
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
85
|
+
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
86
|
+
@returns YES if the comparison (or saving of the reference image) succeeded.
|
87
|
+
*/
|
88
|
+
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
89
|
+
selector:(SEL)selector
|
90
|
+
identifier:(NSString *)identifier
|
91
|
+
error:(NSError **)errorPtr;
|
92
|
+
|
93
|
+
|
94
|
+
/**
|
95
|
+
The directory in which referfence images are stored.
|
96
|
+
*/
|
97
|
+
@property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory;
|
98
|
+
|
99
|
+
/**
|
100
|
+
Loads a reference image.
|
101
|
+
@param selector The test method being run.
|
102
|
+
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
|
103
|
+
@param error An error, if this methods returns nil, the error will be something useful.
|
104
|
+
@returns An image.
|
105
|
+
*/
|
106
|
+
- (UIImage *)referenceImageForSelector:(SEL)selector
|
107
|
+
identifier:(NSString *)identifier
|
108
|
+
error:(NSError **)error;
|
109
|
+
|
110
|
+
/**
|
111
|
+
Saves a reference image.
|
112
|
+
@param selector The test method being run.
|
113
|
+
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
|
114
|
+
@param error An error, if this methods returns NO, the error will be something useful.
|
115
|
+
@returns An image.
|
116
|
+
*/
|
117
|
+
- (BOOL)saveReferenceImage:(UIImage *)image
|
118
|
+
selector:(SEL)selector
|
119
|
+
identifier:(NSString *)identifier
|
120
|
+
error:(NSError **)errorPtr;
|
121
|
+
|
122
|
+
/**
|
123
|
+
Performs a pixel-by-pixel comparison of the two images.
|
124
|
+
@param referenceImage The reference (correct) image.
|
125
|
+
@param image The image to test against the reference.
|
126
|
+
@param error An error that indicates why the comparison failed if it does.
|
127
|
+
@param YES if the comparison succeeded and the images are the same.
|
128
|
+
*/
|
129
|
+
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
|
130
|
+
toImage:(UIImage *)image
|
131
|
+
error:(NSError **)errorPtr;
|
132
|
+
|
133
|
+
/**
|
134
|
+
Saves the reference image and the test image to `failedOutputDirectory`.
|
135
|
+
@param referenceImage The reference (correct) image.
|
136
|
+
@param testImage The image to test against the reference.
|
137
|
+
@param selector The test method being run.
|
138
|
+
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
|
139
|
+
@param error An error that indicates why the comparison failed if it does.
|
140
|
+
@param YES if the save succeeded.
|
141
|
+
*/
|
142
|
+
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
|
143
|
+
testImage:(UIImage *)testImage
|
144
|
+
selector:(SEL)selector
|
145
|
+
identifier:(NSString *)identifier
|
146
|
+
error:(NSError **)errorPtr;
|
147
|
+
@end
|
@@ -0,0 +1,376 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2013, Facebook, Inc.
|
3
|
+
* All rights reserved.
|
4
|
+
*
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
6
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
7
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
8
|
+
*
|
9
|
+
*/
|
10
|
+
|
11
|
+
#import "FBSnapshotTestController.h"
|
12
|
+
|
13
|
+
#import "UIImage+Compare.h"
|
14
|
+
#import "UIImage+Diff.h"
|
15
|
+
|
16
|
+
#import <objc/runtime.h>
|
17
|
+
|
18
|
+
#import <UIKit/UIKit.h>
|
19
|
+
|
20
|
+
NSString *const FBSnapshotTestControllerErrorDomain = @"FBSnapshotTestControllerErrorDomain";
|
21
|
+
|
22
|
+
NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
|
23
|
+
|
24
|
+
typedef struct RGBAPixel {
|
25
|
+
char r;
|
26
|
+
char g;
|
27
|
+
char b;
|
28
|
+
char a;
|
29
|
+
} RGBAPixel;
|
30
|
+
|
31
|
+
@interface FBSnapshotTestController ()
|
32
|
+
|
33
|
+
@property (readonly, nonatomic, retain) Class testClass;
|
34
|
+
|
35
|
+
@end
|
36
|
+
|
37
|
+
@implementation FBSnapshotTestController
|
38
|
+
{
|
39
|
+
NSFileManager *_fileManager;
|
40
|
+
}
|
41
|
+
|
42
|
+
#pragma mark -
|
43
|
+
#pragma mark Lifecycle
|
44
|
+
|
45
|
+
- (id)initWithTestClass:(Class)testClass;
|
46
|
+
{
|
47
|
+
if ((self = [super init])) {
|
48
|
+
_testClass = testClass;
|
49
|
+
_fileManager = [[NSFileManager alloc] init];
|
50
|
+
}
|
51
|
+
return self;
|
52
|
+
}
|
53
|
+
|
54
|
+
#pragma mark -
|
55
|
+
#pragma mark Properties
|
56
|
+
|
57
|
+
- (NSString *)description
|
58
|
+
{
|
59
|
+
return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory];
|
60
|
+
}
|
61
|
+
|
62
|
+
#pragma mark -
|
63
|
+
#pragma mark Public API
|
64
|
+
|
65
|
+
- (UIImage *)referenceImageForSelector:(SEL)selector
|
66
|
+
identifier:(NSString *)identifier
|
67
|
+
error:(NSError **)errorPtr
|
68
|
+
{
|
69
|
+
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
|
70
|
+
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
|
71
|
+
if (nil == image && NULL != errorPtr) {
|
72
|
+
BOOL exists = [_fileManager fileExistsAtPath:filePath];
|
73
|
+
if (!exists) {
|
74
|
+
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
75
|
+
code:FBSnapshotTestControllerErrorCodeNeedsRecord
|
76
|
+
userInfo:@{
|
77
|
+
FBReferenceImageFilePathKey: filePath,
|
78
|
+
NSLocalizedDescriptionKey: @"Unable to load reference image.",
|
79
|
+
NSLocalizedFailureReasonErrorKey: @"Reference image not found. You need to run the test in record mode",
|
80
|
+
}];
|
81
|
+
} else {
|
82
|
+
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
83
|
+
code:FBSnapshotTestControllerErrorCodeUnknown
|
84
|
+
userInfo:nil];
|
85
|
+
}
|
86
|
+
}
|
87
|
+
return image;
|
88
|
+
}
|
89
|
+
|
90
|
+
- (BOOL)saveReferenceImage:(UIImage *)image
|
91
|
+
selector:(SEL)selector
|
92
|
+
identifier:(NSString *)identifier
|
93
|
+
error:(NSError **)errorPtr
|
94
|
+
{
|
95
|
+
BOOL didWrite = NO;
|
96
|
+
if (nil != image) {
|
97
|
+
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
|
98
|
+
NSData *pngData = UIImagePNGRepresentation(image);
|
99
|
+
if (nil != pngData) {
|
100
|
+
NSError *creationError = nil;
|
101
|
+
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
|
102
|
+
withIntermediateDirectories:YES
|
103
|
+
attributes:nil
|
104
|
+
error:&creationError];
|
105
|
+
if (!didCreateDir) {
|
106
|
+
if (NULL != errorPtr) {
|
107
|
+
*errorPtr = creationError;
|
108
|
+
}
|
109
|
+
return NO;
|
110
|
+
}
|
111
|
+
didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr];
|
112
|
+
} else {
|
113
|
+
if (nil != errorPtr) {
|
114
|
+
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
115
|
+
code:FBSnapshotTestControllerErrorCodePNGCreationFailed
|
116
|
+
userInfo:@{
|
117
|
+
FBReferenceImageFilePathKey: filePath,
|
118
|
+
}];
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
return didWrite;
|
123
|
+
}
|
124
|
+
|
125
|
+
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
|
126
|
+
testImage:(UIImage *)testImage
|
127
|
+
selector:(SEL)selector
|
128
|
+
identifier:(NSString *)identifier
|
129
|
+
error:(NSError **)errorPtr
|
130
|
+
{
|
131
|
+
NSData *referencePNGData = UIImagePNGRepresentation(referenceImage);
|
132
|
+
NSData *testPNGData = UIImagePNGRepresentation(testImage);
|
133
|
+
|
134
|
+
NSString *referencePath = [self _failedFilePathForSelector:selector
|
135
|
+
identifier:identifier
|
136
|
+
fileNameType:FBTestSnapshotFileNameTypeFailedReference];
|
137
|
+
|
138
|
+
NSError *creationError = nil;
|
139
|
+
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[referencePath stringByDeletingLastPathComponent]
|
140
|
+
withIntermediateDirectories:YES
|
141
|
+
attributes:nil
|
142
|
+
error:&creationError];
|
143
|
+
if (!didCreateDir) {
|
144
|
+
if (NULL != errorPtr) {
|
145
|
+
*errorPtr = creationError;
|
146
|
+
}
|
147
|
+
return NO;
|
148
|
+
}
|
149
|
+
|
150
|
+
if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) {
|
151
|
+
return NO;
|
152
|
+
}
|
153
|
+
|
154
|
+
NSString *testPath = [self _failedFilePathForSelector:selector
|
155
|
+
identifier:identifier
|
156
|
+
fileNameType:FBTestSnapshotFileNameTypeFailedTest];
|
157
|
+
|
158
|
+
if (![testPNGData writeToFile:testPath options:NSDataWritingAtomic error:errorPtr]) {
|
159
|
+
return NO;
|
160
|
+
}
|
161
|
+
|
162
|
+
NSString *diffPath = [self _failedFilePathForSelector:selector
|
163
|
+
identifier:identifier
|
164
|
+
fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff];
|
165
|
+
|
166
|
+
UIImage *diffImage = [referenceImage diffWithImage:testImage];
|
167
|
+
NSData *diffImageData = UIImagePNGRepresentation(diffImage);
|
168
|
+
|
169
|
+
if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) {
|
170
|
+
return NO;
|
171
|
+
}
|
172
|
+
|
173
|
+
NSLog(@"If you have Kaleidoscope installed you can run this command to see an image diff:\n"
|
174
|
+
@"ksdiff \"%@\" \"%@\"", referencePath, testPath);
|
175
|
+
|
176
|
+
return YES;
|
177
|
+
}
|
178
|
+
|
179
|
+
- (BOOL)compareReferenceImage:(UIImage *)referenceImage toImage:(UIImage *)image error:(NSError **)errorPtr
|
180
|
+
{
|
181
|
+
if (CGSizeEqualToSize(referenceImage.size, image.size)) {
|
182
|
+
|
183
|
+
BOOL imagesEqual = [referenceImage compareWithImage:image];
|
184
|
+
if (NULL != errorPtr) {
|
185
|
+
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
186
|
+
code:FBSnapshotTestControllerErrorCodeImagesDifferent
|
187
|
+
userInfo:@{
|
188
|
+
NSLocalizedDescriptionKey: @"Images different",
|
189
|
+
}];
|
190
|
+
}
|
191
|
+
return imagesEqual;
|
192
|
+
}
|
193
|
+
if (NULL != errorPtr) {
|
194
|
+
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
195
|
+
code:FBSnapshotTestControllerErrorCodeImagesDifferentSizes
|
196
|
+
userInfo:@{
|
197
|
+
NSLocalizedDescriptionKey: @"Images different sizes",
|
198
|
+
NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"referenceImage:%@, image:%@",
|
199
|
+
NSStringFromCGSize(referenceImage.size),
|
200
|
+
NSStringFromCGSize(image.size)],
|
201
|
+
}];
|
202
|
+
}
|
203
|
+
return NO;
|
204
|
+
}
|
205
|
+
|
206
|
+
#pragma mark -
|
207
|
+
#pragma mark Private API
|
208
|
+
|
209
|
+
typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
210
|
+
FBTestSnapshotFileNameTypeReference,
|
211
|
+
FBTestSnapshotFileNameTypeFailedReference,
|
212
|
+
FBTestSnapshotFileNameTypeFailedTest,
|
213
|
+
FBTestSnapshotFileNameTypeFailedTestDiff,
|
214
|
+
};
|
215
|
+
|
216
|
+
- (NSString *)_fileNameForSelector:(SEL)selector
|
217
|
+
identifier:(NSString *)identifier
|
218
|
+
fileNameType:(FBTestSnapshotFileNameType)fileNameType
|
219
|
+
{
|
220
|
+
NSString *fileName = nil;
|
221
|
+
switch (fileNameType) {
|
222
|
+
case FBTestSnapshotFileNameTypeFailedReference:
|
223
|
+
fileName = @"reference_";
|
224
|
+
break;
|
225
|
+
case FBTestSnapshotFileNameTypeFailedTest:
|
226
|
+
fileName = @"failed_";
|
227
|
+
break;
|
228
|
+
case FBTestSnapshotFileNameTypeFailedTestDiff:
|
229
|
+
fileName = @"diff_";
|
230
|
+
break;
|
231
|
+
default:
|
232
|
+
fileName = @"";
|
233
|
+
break;
|
234
|
+
}
|
235
|
+
fileName = [fileName stringByAppendingString:NSStringFromSelector(selector)];
|
236
|
+
if (0 < identifier.length) {
|
237
|
+
fileName = [fileName stringByAppendingFormat:@"_%@", identifier];
|
238
|
+
}
|
239
|
+
if ([[UIScreen mainScreen] scale] >= 2.0) {
|
240
|
+
fileName = [fileName stringByAppendingString:@"@2x"];
|
241
|
+
}
|
242
|
+
fileName = [fileName stringByAppendingPathExtension:@"png"];
|
243
|
+
return fileName;
|
244
|
+
}
|
245
|
+
|
246
|
+
- (NSString *)_referenceFilePathForSelector:(SEL)selector identifier:(NSString *)identifier
|
247
|
+
{
|
248
|
+
NSString *fileName = [self _fileNameForSelector:selector
|
249
|
+
identifier:identifier
|
250
|
+
fileNameType:FBTestSnapshotFileNameTypeReference];
|
251
|
+
NSString *filePath = [_referenceImagesDirectory stringByAppendingPathComponent:NSStringFromClass(_testClass)];
|
252
|
+
filePath = [filePath stringByAppendingPathComponent:fileName];
|
253
|
+
return filePath;
|
254
|
+
}
|
255
|
+
|
256
|
+
- (NSString *)_failedFilePathForSelector:(SEL)selector
|
257
|
+
identifier:(NSString *)identifier
|
258
|
+
fileNameType:(FBTestSnapshotFileNameType)fileNameType
|
259
|
+
{
|
260
|
+
NSString *fileName = [self _fileNameForSelector:selector
|
261
|
+
identifier:identifier
|
262
|
+
fileNameType:fileNameType];
|
263
|
+
NSString *folderPath = NSTemporaryDirectory();
|
264
|
+
if (getenv("IMAGE_DIFF_DIR")) {
|
265
|
+
folderPath = @(getenv("IMAGE_DIFF_DIR"));
|
266
|
+
}
|
267
|
+
NSString *filePath = [folderPath stringByAppendingPathComponent:NSStringFromClass(_testClass)];
|
268
|
+
filePath = [filePath stringByAppendingPathComponent:fileName];
|
269
|
+
return filePath;
|
270
|
+
}
|
271
|
+
|
272
|
+
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
273
|
+
selector:(SEL)selector
|
274
|
+
identifier:(NSString *)identifier
|
275
|
+
error:(NSError **)errorPtr
|
276
|
+
{
|
277
|
+
return [self compareSnapshotOfViewOrLayer:layer
|
278
|
+
selector:selector
|
279
|
+
identifier:identifier
|
280
|
+
error:errorPtr];
|
281
|
+
}
|
282
|
+
|
283
|
+
- (BOOL)compareSnapshotOfView:(UIView *)view
|
284
|
+
selector:(SEL)selector
|
285
|
+
identifier:(NSString *)identifier
|
286
|
+
error:(NSError **)errorPtr
|
287
|
+
{
|
288
|
+
return [self compareSnapshotOfViewOrLayer:view
|
289
|
+
selector:selector
|
290
|
+
identifier:identifier
|
291
|
+
error:errorPtr];
|
292
|
+
}
|
293
|
+
|
294
|
+
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
295
|
+
selector:(SEL)selector
|
296
|
+
identifier:(NSString *)identifier
|
297
|
+
error:(NSError **)errorPtr
|
298
|
+
{
|
299
|
+
if (self.recordMode) {
|
300
|
+
return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr];
|
301
|
+
} else {
|
302
|
+
return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr];
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
#pragma mark -
|
307
|
+
#pragma mark Private API
|
308
|
+
|
309
|
+
- (BOOL)_performPixelComparisonWithViewOrLayer:(UIView *)viewOrLayer
|
310
|
+
selector:(SEL)selector
|
311
|
+
identifier:(NSString *)identifier
|
312
|
+
error:(NSError **)errorPtr
|
313
|
+
{
|
314
|
+
UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr];
|
315
|
+
if (nil != referenceImage) {
|
316
|
+
UIImage *snapshot = [self _snapshotViewOrLayer:viewOrLayer];
|
317
|
+
BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot error:errorPtr];
|
318
|
+
if (!imagesSame) {
|
319
|
+
[self saveFailedReferenceImage:referenceImage
|
320
|
+
testImage:snapshot
|
321
|
+
selector:selector
|
322
|
+
identifier:identifier
|
323
|
+
error:errorPtr];
|
324
|
+
}
|
325
|
+
return imagesSame;
|
326
|
+
}
|
327
|
+
return NO;
|
328
|
+
}
|
329
|
+
|
330
|
+
- (BOOL)_recordSnapshotOfViewOrLayer:(id)viewOrLayer
|
331
|
+
selector:(SEL)selector
|
332
|
+
identifier:(NSString *)identifier
|
333
|
+
error:(NSError **)errorPtr
|
334
|
+
{
|
335
|
+
UIImage *snapshot = [self _snapshotViewOrLayer:viewOrLayer];
|
336
|
+
return [self saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr];
|
337
|
+
}
|
338
|
+
|
339
|
+
- (UIImage *)_snapshotViewOrLayer:(id)viewOrLayer
|
340
|
+
{
|
341
|
+
CALayer *layer = nil;
|
342
|
+
|
343
|
+
if ([viewOrLayer isKindOfClass:[UIView class]]) {
|
344
|
+
UIView *view = (UIView *)viewOrLayer;
|
345
|
+
[view layoutIfNeeded];
|
346
|
+
layer = view.layer;
|
347
|
+
} else if ([viewOrLayer isKindOfClass:[CALayer class]]) {
|
348
|
+
layer = (CALayer *)viewOrLayer;
|
349
|
+
[layer layoutIfNeeded];
|
350
|
+
} else {
|
351
|
+
[NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer];
|
352
|
+
}
|
353
|
+
|
354
|
+
return [self _renderLayer:layer];
|
355
|
+
}
|
356
|
+
|
357
|
+
- (UIImage *)_renderLayer:(CALayer *)layer
|
358
|
+
{
|
359
|
+
CGRect bounds = layer.bounds;
|
360
|
+
|
361
|
+
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
362
|
+
CGContextRef context = UIGraphicsGetCurrentContext();
|
363
|
+
|
364
|
+
CGContextSaveGState(context);
|
365
|
+
{
|
366
|
+
[layer renderInContext:context];
|
367
|
+
}
|
368
|
+
CGContextRestoreGState(context);
|
369
|
+
|
370
|
+
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
|
371
|
+
UIGraphicsEndImageContext();
|
372
|
+
|
373
|
+
return snapshot;
|
374
|
+
}
|
375
|
+
|
376
|
+
@end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
BSD License
|
2
|
+
|
3
|
+
For the FBSnapshotTestCase software
|
4
|
+
|
5
|
+
Copyright (c) 2013, Facebook, Inc.
|
6
|
+
All rights reserved.
|
7
|
+
|
8
|
+
Redistribution and use in source and binary forms, with or without
|
9
|
+
modification, are permitted provided that the following conditions are met:
|
10
|
+
|
11
|
+
* Redistributions of source code must retain the above copyright notice,
|
12
|
+
this list of conditions and the following disclaimer.
|
13
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
14
|
+
this list of conditions and the following disclaimer in the documentation
|
15
|
+
and/or other materials provided with the distribution.
|
16
|
+
* Neither the name Facebook nor the names of its contributors may be used to
|
17
|
+
endorse or promote products derived from this software without specific
|
18
|
+
prior written permission.
|
19
|
+
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@@ -0,0 +1,83 @@
|
|
1
|
+
FBSnapshotTestCase
|
2
|
+
======================
|
3
|
+
|
4
|
+
[![Build Status](https://travis-ci.org/facebook/ios-snapshot-test-case.png)](https://travis-ci.org/facebook/ios-snapshot-test-case)
|
5
|
+
|
6
|
+
What it does
|
7
|
+
------------
|
8
|
+
|
9
|
+
A "snapshot test case" takes a configured `UIView` or `CALayer` and uses the
|
10
|
+
`renderInContext:` method to get an image snapshot of its contents. It
|
11
|
+
compares this snapshot to a "reference image" stored in your source code
|
12
|
+
repository and fails the test if the two images don't match.
|
13
|
+
|
14
|
+
Why?
|
15
|
+
----
|
16
|
+
|
17
|
+
At Facebook we write a lot of UI code. As you might imagine, each type of
|
18
|
+
feed story is rendered using a subclass of `UIView`. There are a lot of edge
|
19
|
+
cases that we want to handle correctly:
|
20
|
+
|
21
|
+
- What if there is more text than can fit in the space available?
|
22
|
+
- What if an image doesn't match the size of an image view?
|
23
|
+
- What should the highlighted state look like?
|
24
|
+
|
25
|
+
It's straightforward to test logic code, but less obvious how you should test
|
26
|
+
views. You can do a lot of rectangle asserts, but these are hard to understand
|
27
|
+
or visualize. Looking at an image diff shows you exactly what changed and how
|
28
|
+
it will look to users.
|
29
|
+
|
30
|
+
We developed `FBSnapshotTestCase` to make snapshot tests easy.
|
31
|
+
|
32
|
+
Creating a snapshot test
|
33
|
+
------------------------
|
34
|
+
|
35
|
+
1. Drop the `FBSnapshotTestCase` and `FBSnapshotTestController` source files
|
36
|
+
into your project. In the Add Files dialog, be sure to check the **tests**
|
37
|
+
target, not the main target.
|
38
|
+
|
39
|
+
<img src="http://facebook.github.io/ios-snapshot-test-case/AddFiles.png" alt="Add Files Dialog" width="364">
|
40
|
+
|
41
|
+
2. Define `FB_REFERENCE_IMAGE_DIR` in `GCC_PREPROCESSOR_DEFINITIONS`. This
|
42
|
+
should point to the directory where you want reference images to be stored.
|
43
|
+
At Facebook, we normally use this:
|
44
|
+
|
45
|
+
`GCC_PREPROCESSOR_DEFINITIONS = $(inherited) FB_REFERENCE_IMAGE_DIR="\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\""`
|
46
|
+
|
47
|
+
3. Subclass `FBSnapshotTestCase` instead of `XCTestCase`.
|
48
|
+
4. From within your test, use `FBSnapshotVerifyView`.
|
49
|
+
5. Run the test once with `self.recordMode = YES;` in the test's `-setUp`
|
50
|
+
method. (This creates the reference images on disk.)
|
51
|
+
6. Remove the line enabling record mode and run the test.
|
52
|
+
|
53
|
+
Features
|
54
|
+
--------
|
55
|
+
|
56
|
+
- Automatically names reference images on disk according to test class and
|
57
|
+
selector.
|
58
|
+
- Prints a descriptive error message to the console on failure. (Bonus:
|
59
|
+
failure message includes a one-line command to see an image diff if
|
60
|
+
you have [Kaleidoscope](http://www.kaleidoscopeapp.com) installed.)
|
61
|
+
- Supply an optional "identifier" if you want to perform multiple snapshots
|
62
|
+
in a single test method.
|
63
|
+
- Support for `CALayer` via `FBSnapshotVerifyLayer`.
|
64
|
+
|
65
|
+
Notes
|
66
|
+
-----
|
67
|
+
|
68
|
+
Your unit test must be an "application test", not a "logic test." (That is, it
|
69
|
+
must be run within the Simulator so that it has access to UIKit.) In Xcode 5
|
70
|
+
and later new projects only offer application tests, but older projects will
|
71
|
+
have separate targets for the two types.
|
72
|
+
|
73
|
+
Authors
|
74
|
+
-------
|
75
|
+
|
76
|
+
`FBSnapshotTestCase` was written at Facebook by
|
77
|
+
[Jonathan Dann](https://facebook.com/j.p.dann) with significant contributions by
|
78
|
+
[Todd Krabach](https://facebook.com/toddkrabach).
|
79
|
+
|
80
|
+
License
|
81
|
+
-------
|
82
|
+
|
83
|
+
`FBSnapshotTestCase` is BSD-licensed. See `LICENSE`.
|
@@ -0,0 +1,37 @@
|
|
1
|
+
//
|
2
|
+
// Created by Gabriel Handford on 3/1/09.
|
3
|
+
// Copyright 2009-2013. All rights reserved.
|
4
|
+
// Created by John Boiles on 10/20/11.
|
5
|
+
// Copyright (c) 2011. All rights reserved
|
6
|
+
// Modified by Felix Schulze on 2/11/13.
|
7
|
+
// Copyright 2013. All rights reserved.
|
8
|
+
//
|
9
|
+
// Permission is hereby granted, free of charge, to any person
|
10
|
+
// obtaining a copy of this software and associated documentation
|
11
|
+
// files (the "Software"), to deal in the Software without
|
12
|
+
// restriction, including without limitation the rights to use,
|
13
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
// copies of the Software, and to permit persons to whom the
|
15
|
+
// Software is furnished to do so, subject to the following
|
16
|
+
// conditions:
|
17
|
+
//
|
18
|
+
// The above copyright notice and this permission notice shall be
|
19
|
+
// included in all copies or substantial portions of the Software.
|
20
|
+
//
|
21
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
22
|
+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
23
|
+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
24
|
+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
25
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
26
|
+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
27
|
+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
28
|
+
// OTHER DEALINGS IN THE SOFTWARE.
|
29
|
+
//
|
30
|
+
|
31
|
+
#import <UIKit/UIKit.h>
|
32
|
+
|
33
|
+
@interface UIImage (Compare)
|
34
|
+
|
35
|
+
- (BOOL)compareWithImage:(UIImage *)image;
|
36
|
+
|
37
|
+
@end
|