second_curtain 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +20 -0
  3. data/.travis.yml +7 -0
  4. data/CHANGELOG +20 -0
  5. data/Demo/Demo/ASHAppDelegate.h +15 -0
  6. data/Demo/Demo/ASHAppDelegate.m +18 -0
  7. data/Demo/Demo/ASHViewController.h +13 -0
  8. data/Demo/Demo/ASHViewController.m +30 -0
  9. data/Demo/Demo/Base.lproj/Main.storyboard +30 -0
  10. data/Demo/Demo/Demo-Info.plist +40 -0
  11. data/Demo/Demo/Demo-Prefix.pch +16 -0
  12. data/Demo/Demo/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  13. data/Demo/Demo/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  14. data/Demo/Demo/en.lproj/InfoPlist.strings +2 -0
  15. data/Demo/Demo/main.m +18 -0
  16. data/Demo/Demo.xcodeproj/project.pbxproj +517 -0
  17. data/Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  18. data/Demo/Demo.xcodeproj/project.xcworkspace/xcshareddata/Demo.xccheckout +41 -0
  19. data/Demo/Demo.xcodeproj/project.xcworkspace/xcuserdata/ash.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  20. data/Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme +96 -0
  21. data/Demo/Demo.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
  22. data/Demo/Demo.xcodeproj/xcuserdata/orta.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
  23. data/Demo/Demo.xcworkspace/contents.xcworkspacedata +1 -0
  24. data/Demo/Demo.xcworkspace/xcshareddata/Demo.xccheckout +41 -0
  25. data/Demo/Demo.xcworkspace/xcuserdata/ash.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  26. data/Demo/Demo.xcworkspace/xcuserdata/orta.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  27. data/Demo/DemoTests/DemoTests-Info.plist +22 -0
  28. data/Demo/DemoTests/DemoTests.m +52 -0
  29. data/Demo/DemoTests/ReferenceImages/ASHViewControllerSpec/a_view_controller_with_a_loaded_view_should_have_a_valid_snapshot@2x.png +0 -0
  30. data/Demo/DemoTests/ReferenceImages/ASHViewControllerSpec/views_should_be_green@2x.png +0 -0
  31. data/Demo/DemoTests/ReferenceImages/ASHViewControllerSpec/views_should_be_red@2x.png +0 -0
  32. data/Demo/DemoTests/en.lproj/InfoPlist.strings +2 -0
  33. data/Demo/Makefile +15 -0
  34. data/Demo/Podfile +16 -0
  35. data/Demo/Podfile.lock +21 -0
  36. data/Demo/Pods/BuildHeaders/Expecta/EXPBackwardCompatibility.h +42 -0
  37. data/Demo/Pods/BuildHeaders/Expecta/EXPBlockDefinedMatcher.h +25 -0
  38. data/Demo/Pods/BuildHeaders/Expecta/EXPDefines.h +17 -0
  39. data/Demo/Pods/BuildHeaders/Expecta/EXPDoubleTuple.h +13 -0
  40. data/Demo/Pods/BuildHeaders/Expecta/EXPExpect.h +45 -0
  41. data/Demo/Pods/BuildHeaders/Expecta/EXPFloatTuple.h +13 -0
  42. data/Demo/Pods/BuildHeaders/Expecta/EXPMatcher.h +20 -0
  43. data/Demo/Pods/BuildHeaders/Expecta/EXPMatcherHelpers.h +4 -0
  44. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beCloseTo.h +7 -0
  45. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beFalsy.h +3 -0
  46. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beGreaterThan.h +6 -0
  47. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beGreaterThanOrEqualTo.h +6 -0
  48. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beIdenticalTo.h +10 -0
  49. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beInTheRangeOf.h +6 -0
  50. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beInstanceOf.h +7 -0
  51. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beKindOf.h +5 -0
  52. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beLessThan.h +6 -0
  53. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beLessThanOrEqualTo.h +6 -0
  54. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beNil.h +5 -0
  55. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beSubclassOf.h +5 -0
  56. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beSupersetOf.h +4 -0
  57. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beTruthy.h +3 -0
  58. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+beginWith.h +5 -0
  59. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+conformTo.h +3 -0
  60. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+contain.h +5 -0
  61. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+endWith.h +3 -0
  62. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+equal.h +5 -0
  63. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+haveCountOf.h +10 -0
  64. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+notify.h +4 -0
  65. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+raise.h +4 -0
  66. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+raiseWithReason.h +3 -0
  67. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers+respondTo.h +3 -0
  68. data/Demo/Pods/BuildHeaders/Expecta/EXPMatchers.h +24 -0
  69. data/Demo/Pods/BuildHeaders/Expecta/EXPUnsupportedObject.h +11 -0
  70. data/Demo/Pods/BuildHeaders/Expecta/Expecta.h +27 -0
  71. data/Demo/Pods/BuildHeaders/Expecta/ExpectaSupport.h +64 -0
  72. data/Demo/Pods/BuildHeaders/Expecta/NSObject+Expecta.h +14 -0
  73. data/Demo/Pods/BuildHeaders/Expecta/NSValue+Expecta.h +8 -0
  74. data/Demo/Pods/BuildHeaders/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h +21 -0
  75. data/Demo/Pods/BuildHeaders/FBSnapshotTestCase/FBSnapshotTestCase.h +87 -0
  76. data/Demo/Pods/BuildHeaders/FBSnapshotTestCase/FBSnapshotTestController.h +147 -0
  77. data/Demo/Pods/BuildHeaders/FBSnapshotTestCase/UIImage+Compare.h +37 -0
  78. data/Demo/Pods/BuildHeaders/FBSnapshotTestCase/UIImage+Diff.h +37 -0
  79. data/Demo/Pods/BuildHeaders/Specta/SPTExample.h +14 -0
  80. data/Demo/Pods/BuildHeaders/Specta/SPTExampleGroup.h +40 -0
  81. data/Demo/Pods/BuildHeaders/Specta/SPTNestedReporter.h +5 -0
  82. data/Demo/Pods/BuildHeaders/Specta/SPTReporter.h +33 -0
  83. data/Demo/Pods/BuildHeaders/Specta/SPTSharedExampleGroups.h +17 -0
  84. data/Demo/Pods/BuildHeaders/Specta/SPTSpec.h +21 -0
  85. data/Demo/Pods/BuildHeaders/Specta/SPTXCTestCase.h +27 -0
  86. data/Demo/Pods/BuildHeaders/Specta/SPTXCTestReporter.h +9 -0
  87. data/Demo/Pods/BuildHeaders/Specta/Specta.h +70 -0
  88. data/Demo/Pods/BuildHeaders/Specta/SpectaSupport.h +46 -0
  89. data/Demo/Pods/BuildHeaders/Specta/SpectaTypes.h +3 -0
  90. data/Demo/Pods/BuildHeaders/Specta/SpectaUtility.h +14 -0
  91. data/Demo/Pods/BuildHeaders/Specta/XCTestCase+Specta.h +4 -0
  92. data/Demo/Pods/BuildHeaders/Specta/XCTestLog+Specta.h +7 -0
  93. data/Demo/Pods/BuildHeaders/Specta/XCTestRun+Specta.h +8 -0
  94. data/Demo/Pods/Expecta/LICENSE +19 -0
  95. data/Demo/Pods/Expecta/README.md +308 -0
  96. data/Demo/Pods/Expecta/src/EXPBackwardCompatibility.h +42 -0
  97. data/Demo/Pods/Expecta/src/EXPBackwardCompatibility.m +17 -0
  98. data/Demo/Pods/Expecta/src/EXPBlockDefinedMatcher.h +25 -0
  99. data/Demo/Pods/Expecta/src/EXPBlockDefinedMatcher.m +60 -0
  100. data/Demo/Pods/Expecta/src/EXPDefines.h +17 -0
  101. data/Demo/Pods/Expecta/src/EXPDoubleTuple.h +13 -0
  102. data/Demo/Pods/Expecta/src/EXPDoubleTuple.m +42 -0
  103. data/Demo/Pods/Expecta/src/EXPExpect.h +45 -0
  104. data/Demo/Pods/Expecta/src/EXPExpect.m +214 -0
  105. data/Demo/Pods/Expecta/src/EXPFloatTuple.h +13 -0
  106. data/Demo/Pods/Expecta/src/EXPFloatTuple.m +42 -0
  107. data/Demo/Pods/Expecta/src/EXPMatcher.h +20 -0
  108. data/Demo/Pods/Expecta/src/EXPUnsupportedObject.h +11 -0
  109. data/Demo/Pods/Expecta/src/EXPUnsupportedObject.m +20 -0
  110. data/Demo/Pods/Expecta/src/Expecta.h +27 -0
  111. data/Demo/Pods/Expecta/src/Expecta.m +15 -0
  112. data/Demo/Pods/Expecta/src/ExpectaSupport.h +64 -0
  113. data/Demo/Pods/Expecta/src/ExpectaSupport.m +187 -0
  114. data/Demo/Pods/Expecta/src/NSObject+Expecta.h +14 -0
  115. data/Demo/Pods/Expecta/src/NSValue+Expecta.h +8 -0
  116. data/Demo/Pods/Expecta/src/NSValue+Expecta.m +21 -0
  117. data/Demo/Pods/Expecta/src/matchers/EXPMatcherHelpers.h +4 -0
  118. data/Demo/Pods/Expecta/src/matchers/EXPMatcherHelpers.m +9 -0
  119. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beCloseTo.h +7 -0
  120. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beCloseTo.m +49 -0
  121. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beFalsy.h +3 -0
  122. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beFalsy.m +24 -0
  123. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beGreaterThan.h +6 -0
  124. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beGreaterThan.m +20 -0
  125. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beGreaterThanOrEqualTo.h +6 -0
  126. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beGreaterThanOrEqualTo.m +20 -0
  127. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beIdenticalTo.h +10 -0
  128. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beIdenticalTo.m +24 -0
  129. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beInTheRangeOf.h +6 -0
  130. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beInTheRangeOf.m +30 -0
  131. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beInstanceOf.h +7 -0
  132. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beInstanceOf.m +27 -0
  133. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beKindOf.h +5 -0
  134. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beKindOf.m +27 -0
  135. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beLessThan.h +6 -0
  136. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beLessThan.m +20 -0
  137. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beLessThanOrEqualTo.h +6 -0
  138. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beLessThanOrEqualTo.m +20 -0
  139. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beNil.h +5 -0
  140. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beNil.m +16 -0
  141. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beSubclassOf.h +5 -0
  142. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beSubclassOf.m +27 -0
  143. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beSupersetOf.h +4 -0
  144. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beSupersetOf.m +57 -0
  145. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beTruthy.h +3 -0
  146. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beTruthy.m +24 -0
  147. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beginWith.h +5 -0
  148. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+beginWith.m +49 -0
  149. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+conformTo.h +3 -0
  150. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+conformTo.m +33 -0
  151. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+contain.h +5 -0
  152. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+contain.m +38 -0
  153. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+endWith.h +3 -0
  154. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+endWith.m +49 -0
  155. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+equal.h +5 -0
  156. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+equal.m +24 -0
  157. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+haveCountOf.h +10 -0
  158. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+haveCountOf.m +36 -0
  159. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+notify.h +4 -0
  160. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+notify.m +63 -0
  161. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+raise.h +4 -0
  162. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+raise.m +30 -0
  163. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+raiseWithReason.h +3 -0
  164. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+raiseWithReason.m +35 -0
  165. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+respondTo.h +3 -0
  166. data/Demo/Pods/Expecta/src/matchers/EXPMatchers+respondTo.m +28 -0
  167. data/Demo/Pods/Expecta/src/matchers/EXPMatchers.h +24 -0
  168. data/Demo/Pods/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h +21 -0
  169. data/Demo/Pods/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.m +308 -0
  170. data/Demo/Pods/Expecta+Snapshots/LICENSE.md +22 -0
  171. data/Demo/Pods/Expecta+Snapshots/README.md +85 -0
  172. data/Demo/Pods/FBSnapshotTestCase/FBSnapshotTestCase.h +87 -0
  173. data/Demo/Pods/FBSnapshotTestCase/FBSnapshotTestCase.m +82 -0
  174. data/Demo/Pods/FBSnapshotTestCase/FBSnapshotTestController.h +147 -0
  175. data/Demo/Pods/FBSnapshotTestCase/FBSnapshotTestController.m +376 -0
  176. data/Demo/Pods/FBSnapshotTestCase/LICENSE +29 -0
  177. data/Demo/Pods/FBSnapshotTestCase/README.md +83 -0
  178. data/Demo/Pods/FBSnapshotTestCase/UIImage+Compare.h +37 -0
  179. data/Demo/Pods/FBSnapshotTestCase/UIImage+Compare.m +87 -0
  180. data/Demo/Pods/FBSnapshotTestCase/UIImage+Diff.h +37 -0
  181. data/Demo/Pods/FBSnapshotTestCase/UIImage+Diff.m +56 -0
  182. data/Demo/Pods/Headers/Expecta/EXPBackwardCompatibility.h +42 -0
  183. data/Demo/Pods/Headers/Expecta/EXPBlockDefinedMatcher.h +25 -0
  184. data/Demo/Pods/Headers/Expecta/EXPDefines.h +17 -0
  185. data/Demo/Pods/Headers/Expecta/EXPDoubleTuple.h +13 -0
  186. data/Demo/Pods/Headers/Expecta/EXPExpect.h +45 -0
  187. data/Demo/Pods/Headers/Expecta/EXPFloatTuple.h +13 -0
  188. data/Demo/Pods/Headers/Expecta/EXPMatcher.h +20 -0
  189. data/Demo/Pods/Headers/Expecta/EXPMatcherHelpers.h +4 -0
  190. data/Demo/Pods/Headers/Expecta/EXPMatchers+beCloseTo.h +7 -0
  191. data/Demo/Pods/Headers/Expecta/EXPMatchers+beFalsy.h +3 -0
  192. data/Demo/Pods/Headers/Expecta/EXPMatchers+beGreaterThan.h +6 -0
  193. data/Demo/Pods/Headers/Expecta/EXPMatchers+beGreaterThanOrEqualTo.h +6 -0
  194. data/Demo/Pods/Headers/Expecta/EXPMatchers+beIdenticalTo.h +10 -0
  195. data/Demo/Pods/Headers/Expecta/EXPMatchers+beInTheRangeOf.h +6 -0
  196. data/Demo/Pods/Headers/Expecta/EXPMatchers+beInstanceOf.h +7 -0
  197. data/Demo/Pods/Headers/Expecta/EXPMatchers+beKindOf.h +5 -0
  198. data/Demo/Pods/Headers/Expecta/EXPMatchers+beLessThan.h +6 -0
  199. data/Demo/Pods/Headers/Expecta/EXPMatchers+beLessThanOrEqualTo.h +6 -0
  200. data/Demo/Pods/Headers/Expecta/EXPMatchers+beNil.h +5 -0
  201. data/Demo/Pods/Headers/Expecta/EXPMatchers+beSubclassOf.h +5 -0
  202. data/Demo/Pods/Headers/Expecta/EXPMatchers+beSupersetOf.h +4 -0
  203. data/Demo/Pods/Headers/Expecta/EXPMatchers+beTruthy.h +3 -0
  204. data/Demo/Pods/Headers/Expecta/EXPMatchers+beginWith.h +5 -0
  205. data/Demo/Pods/Headers/Expecta/EXPMatchers+conformTo.h +3 -0
  206. data/Demo/Pods/Headers/Expecta/EXPMatchers+contain.h +5 -0
  207. data/Demo/Pods/Headers/Expecta/EXPMatchers+endWith.h +3 -0
  208. data/Demo/Pods/Headers/Expecta/EXPMatchers+equal.h +5 -0
  209. data/Demo/Pods/Headers/Expecta/EXPMatchers+haveCountOf.h +10 -0
  210. data/Demo/Pods/Headers/Expecta/EXPMatchers+notify.h +4 -0
  211. data/Demo/Pods/Headers/Expecta/EXPMatchers+raise.h +4 -0
  212. data/Demo/Pods/Headers/Expecta/EXPMatchers+raiseWithReason.h +3 -0
  213. data/Demo/Pods/Headers/Expecta/EXPMatchers+respondTo.h +3 -0
  214. data/Demo/Pods/Headers/Expecta/EXPMatchers.h +24 -0
  215. data/Demo/Pods/Headers/Expecta/EXPUnsupportedObject.h +11 -0
  216. data/Demo/Pods/Headers/Expecta/Expecta.h +27 -0
  217. data/Demo/Pods/Headers/Expecta/ExpectaSupport.h +64 -0
  218. data/Demo/Pods/Headers/Expecta/NSObject+Expecta.h +14 -0
  219. data/Demo/Pods/Headers/Expecta/NSValue+Expecta.h +8 -0
  220. data/Demo/Pods/Headers/Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h +21 -0
  221. data/Demo/Pods/Headers/FBSnapshotTestCase/FBSnapshotTestCase.h +87 -0
  222. data/Demo/Pods/Headers/FBSnapshotTestCase/FBSnapshotTestController.h +147 -0
  223. data/Demo/Pods/Headers/FBSnapshotTestCase/UIImage+Compare.h +37 -0
  224. data/Demo/Pods/Headers/FBSnapshotTestCase/UIImage+Diff.h +37 -0
  225. data/Demo/Pods/Headers/Specta/SPTExample.h +14 -0
  226. data/Demo/Pods/Headers/Specta/SPTExampleGroup.h +40 -0
  227. data/Demo/Pods/Headers/Specta/SPTNestedReporter.h +5 -0
  228. data/Demo/Pods/Headers/Specta/SPTReporter.h +33 -0
  229. data/Demo/Pods/Headers/Specta/SPTSharedExampleGroups.h +17 -0
  230. data/Demo/Pods/Headers/Specta/SPTSpec.h +21 -0
  231. data/Demo/Pods/Headers/Specta/SPTXCTestCase.h +27 -0
  232. data/Demo/Pods/Headers/Specta/SPTXCTestReporter.h +9 -0
  233. data/Demo/Pods/Headers/Specta/Specta.h +70 -0
  234. data/Demo/Pods/Headers/Specta/SpectaSupport.h +46 -0
  235. data/Demo/Pods/Headers/Specta/SpectaTypes.h +3 -0
  236. data/Demo/Pods/Headers/Specta/SpectaUtility.h +14 -0
  237. data/Demo/Pods/Headers/Specta/XCTestCase+Specta.h +4 -0
  238. data/Demo/Pods/Headers/Specta/XCTestLog+Specta.h +7 -0
  239. data/Demo/Pods/Headers/Specta/XCTestRun+Specta.h +8 -0
  240. data/Demo/Pods/Manifest.lock +21 -0
  241. data/Demo/Pods/Pods-DemoTests-EXPMatchers+FBSnapshotTest-Private.xcconfig +6 -0
  242. data/Demo/Pods/Pods-DemoTests-EXPMatchers+FBSnapshotTest-dummy.m +5 -0
  243. data/Demo/Pods/Pods-DemoTests-EXPMatchers+FBSnapshotTest-prefix.pch +5 -0
  244. data/Demo/Pods/Pods-DemoTests-EXPMatchers+FBSnapshotTest.xcconfig +2 -0
  245. data/Demo/Pods/Pods-DemoTests-Expecta+Snapshots-Private.xcconfig +6 -0
  246. data/Demo/Pods/Pods-DemoTests-Expecta+Snapshots-dummy.m +5 -0
  247. data/Demo/Pods/Pods-DemoTests-Expecta+Snapshots-prefix.pch +5 -0
  248. data/Demo/Pods/Pods-DemoTests-Expecta+Snapshots.xcconfig +2 -0
  249. data/Demo/Pods/Pods-DemoTests-Expecta-Private.xcconfig +5 -0
  250. data/Demo/Pods/Pods-DemoTests-Expecta-dummy.m +5 -0
  251. data/Demo/Pods/Pods-DemoTests-Expecta-prefix.pch +5 -0
  252. data/Demo/Pods/Pods-DemoTests-Expecta.xcconfig +1 -0
  253. data/Demo/Pods/Pods-DemoTests-FBSnapshotTestCase-Private.xcconfig +6 -0
  254. data/Demo/Pods/Pods-DemoTests-FBSnapshotTestCase-dummy.m +5 -0
  255. data/Demo/Pods/Pods-DemoTests-FBSnapshotTestCase-prefix.pch +6 -0
  256. data/Demo/Pods/Pods-DemoTests-FBSnapshotTestCase.xcconfig +2 -0
  257. data/Demo/Pods/Pods-DemoTests-Specta-Private.xcconfig +6 -0
  258. data/Demo/Pods/Pods-DemoTests-Specta-dummy.m +5 -0
  259. data/Demo/Pods/Pods-DemoTests-Specta-prefix.pch +5 -0
  260. data/Demo/Pods/Pods-DemoTests-Specta.xcconfig +2 -0
  261. data/Demo/Pods/Pods-DemoTests-acknowledgements.markdown +109 -0
  262. data/Demo/Pods/Pods-DemoTests-acknowledgements.plist +151 -0
  263. data/Demo/Pods/Pods-DemoTests-dummy.m +5 -0
  264. data/Demo/Pods/Pods-DemoTests-environment.h +32 -0
  265. data/Demo/Pods/Pods-DemoTests-resources.sh +68 -0
  266. data/Demo/Pods/Pods-DemoTests.xcconfig +6 -0
  267. data/Demo/Pods/Pods.xcodeproj/project.pbxproj +1390 -0
  268. data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/Pods-DemoTests-Expecta+Snapshots.xcscheme +59 -0
  269. data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/Pods-DemoTests-Expecta.xcscheme +59 -0
  270. data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/Pods-DemoTests-FBSnapshotTestCase.xcscheme +59 -0
  271. data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/Pods-DemoTests-Specta.xcscheme +59 -0
  272. data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/Pods-DemoTests.xcscheme +59 -0
  273. data/Demo/Pods/Pods.xcodeproj/xcuserdata/ash.xcuserdatad/xcschemes/xcschememanagement.plist +62 -0
  274. data/Demo/Pods/Specta/LICENSE +20 -0
  275. data/Demo/Pods/Specta/README.md +146 -0
  276. data/Demo/Pods/Specta/src/SPTExample.h +14 -0
  277. data/Demo/Pods/Specta/src/SPTExample.m +16 -0
  278. data/Demo/Pods/Specta/src/SPTExampleGroup.h +40 -0
  279. data/Demo/Pods/Specta/src/SPTExampleGroup.m +326 -0
  280. data/Demo/Pods/Specta/src/SPTNestedReporter.h +5 -0
  281. data/Demo/Pods/Specta/src/SPTNestedReporter.m +194 -0
  282. data/Demo/Pods/Specta/src/SPTReporter.h +33 -0
  283. data/Demo/Pods/Specta/src/SPTReporter.m +134 -0
  284. data/Demo/Pods/Specta/src/SPTSharedExampleGroups.h +17 -0
  285. data/Demo/Pods/Specta/src/SPTSharedExampleGroups.m +62 -0
  286. data/Demo/Pods/Specta/src/SPTSpec.h +21 -0
  287. data/Demo/Pods/Specta/src/SPTSpec.m +31 -0
  288. data/Demo/Pods/Specta/src/SPTXCTestCase.h +27 -0
  289. data/Demo/Pods/Specta/src/SPTXCTestCase.m +151 -0
  290. data/Demo/Pods/Specta/src/SPTXCTestReporter.h +9 -0
  291. data/Demo/Pods/Specta/src/SPTXCTestReporter.m +11 -0
  292. data/Demo/Pods/Specta/src/Specta.h +70 -0
  293. data/Demo/Pods/Specta/src/Specta.m +144 -0
  294. data/Demo/Pods/Specta/src/SpectaSupport.h +46 -0
  295. data/Demo/Pods/Specta/src/SpectaTypes.h +3 -0
  296. data/Demo/Pods/Specta/src/SpectaUtility.h +14 -0
  297. data/Demo/Pods/Specta/src/SpectaUtility.m +65 -0
  298. data/Demo/Pods/Specta/src/XCTestCase+Specta.h +4 -0
  299. data/Demo/Pods/Specta/src/XCTestCase+Specta.m +26 -0
  300. data/Demo/Pods/Specta/src/XCTestLog+Specta.h +7 -0
  301. data/Demo/Pods/Specta/src/XCTestLog+Specta.m +49 -0
  302. data/Demo/Pods/Specta/src/XCTestRun+Specta.h +8 -0
  303. data/Demo/Pods/Specta/src/XCTestRun+Specta.m +44 -0
  304. data/Gemfile +5 -0
  305. data/LICENSE +21 -0
  306. data/README.md +146 -0
  307. data/Rakefile +7 -0
  308. data/second_curtain.gemspec +20 -0
  309. data/spec/sample_output.txt +79 -0
  310. data/spec/second_shutter/kaleidoscope_command_spec.rb +25 -0
  311. data/spec/second_shutter/parser_spec.rb +39 -0
  312. data/spec/second_shutter/test_suite_spec.rb +32 -0
  313. data/spec/second_shutter/upload_manager_spec.rb +60 -0
  314. data/spec/second_shutter/upload_spec.rb +34 -0
  315. data/spec/second_shutter/xcode_test_case_spec.rb +22 -0
  316. 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