Compare commits

..

168 Commits

Author SHA1 Message Date
dcvz 77fb2b88d3 Bump version to 1.0.0-rc.9 2023-09-22 10:21:49 +02:00
dcvz 5ff8c9dffc Use async loading of metadata asset keys 2023-09-22 10:13:14 +02:00
dcvz 077d4b1d53 Bump version to 1.0.0-rc.8 2023-09-18 17:33:59 +02:00
dcvz 05322d9887 Fix rate updates in notification 2023-09-18 17:22:08 +02:00
dcvz 645b7bc8e7 Bump version to 1.0.0-rc.6 2023-09-07 11:45:34 +02:00
Hai Phung N.T e64e658b3b Timer is not showing properly from the notification center (#60) 2023-09-04 20:43:16 +02:00
Kirill Zyusko bf8e54e6a6 fix: repeat mode (pt. 2) (#62) 2023-09-04 20:41:36 +02:00
dcvz ed9fe280db Bump version to 1.0.0-rc.6 2023-08-15 11:20:42 +02:00
David Chavez 1148a6c28b chore(metadata): Handle more metadata types (#58) 2023-08-15 11:15:03 +02:00
David Chavez 9b6dcff4e2 Bump version to 1.0.0-rc.5 2023-07-25 18:12:26 +02:00
David Chavez bfe5851dc4 fix(replay): replay at bottom of stack 2023-07-25 18:10:27 +02:00
David Chavez 7ffa9b0113 chore(tests): fix comparsion approximations 2023-07-25 12:16:13 +02:00
Jonathan Puckey ebec7afccd Fix/race conditions (#55) 2023-07-18 12:23:04 +02:00
David Chavez 0fa786a91c Bump version to 1.0.0-rc.4 2023-06-20 14:04:05 +02:00
Jakub Bogacki 8fb5c66820 Fix QueuedAudioPlayer.currentItem.getter crash (#48)
Co-authored-by: David Chavez <david@dcvz.io>
2023-06-20 14:02:56 +02:00
David Chavez 42693b6dfb Bump version to 1.0.0-rc.3 2023-03-27 10:02:33 +02:00
Jonathan Puckey 348dcc17f7 Fix player state not becoming paused after loading (#46)
- fix player state not becoming .paused after .loading
- change tests to reflect the correct result
- set async defaults timeout to 10 seconds & polling to 100ms
- refactor AudioPlayerTests to use PlayerStateEventListener, quick & nimble
2023-03-27 09:57:51 +02:00
Jonathan Puckey b10aea494f Fix/queue manager (#45)
- avoid calling delegate?.onSkippedToSameCurrentItem() when the current index becomes one less due to a track before it being removed (the root cause of what fix: #42 - update current item correctly upon removal #43 fixes)
- avoid calling onCurrentItemChanged unnecessarily in skip
- move onSkippedToSameCurrentItem() call into skip & jump
- fix a few tests
2023-03-27 09:56:44 +02:00
David Chavez cbbbd57397 Revert "Fix removing items from queue other than the current track (#41)"
This reverts commit a270b3b232.
2023-03-27 09:56:09 +02:00
Christian Duvholt a270b3b232 Fix removing items from queue other than the current track (#41)
Co-authored-by: Christian Duvholt <christian.duvholt@iterate.no>
2023-03-27 09:51:50 +02:00
David Chavez 3cac61fe8f Bump version to 1.0.0-rc.2 2023-02-21 15:54:26 +01:00
Jonathan Puckey 7870d3bba6 Fix seeking within a large file over http & updating of playWhenReady to external pause (#40) 2023-02-20 19:07:38 +01:00
Alexey Strelkov 4c891bcdc6 Fix installation instructions via cocoapods (#32) 2023-02-10 10:33:42 +01:00
Philip Su 9e114360ec fix: update README to not refer to pod install for Example (#35)
The sample project doesn't have a Podfile, and it also seems to build/run just fine without `pod install` after cloning the repo. So I've updated the README to reflect that.
2023-02-10 10:32:28 +01:00
David Chavez f2c9a272d9 Bump version to 1.0.0-rc.1 2023-01-24 15:57:13 +01:00
Jonathan Puckey e41bb22a48 Feature: Queue improvements (#28) 2023-01-24 15:50:48 +01:00
David Chavez 23fdb9b9db Release 0.15.3 2022-09-14 15:02:30 +02:00
Jonathan Puckey 24c19aa661 Remove unnecessary buffer settings from tests. (#26)
As expected, the tests run successfully without these set too. See https://github.com/doublesymmetry/react-native-track-player/pull/1695
2022-09-06 09:15:36 +02:00
Jonathan Puckey 38429c6ca8 Reset AVPlayerWrapper on failure to load pending asset (#25)
See https://github.com/doublesymmetry/react-native-track-player/issues/1538
2022-09-06 09:13:38 +02:00
Jonathan Puckey 72f9c5d147 Fix order of AVPlayerWrapperState.state (#21) 2022-09-06 09:02:45 +02:00
David Chavez bd93898809 fix(tests): run workflows on PRs (#27) 2022-09-06 08:58:59 +02:00
David Chavez 8276f38b1b Release 0.15.2 2022-05-07 21:14:56 +02:00
David Akpan fcd5790e1e fix/ios-hls-live-duration (#18) 2022-05-07 08:59:17 +02:00
Jacob Spizziri ead7c0962e fix(audioplayer): fix loadArtwork method to unset artwork value if no image is given (#17)
https://github.com/doublesymmetry/react-native-track-player/issues/1511
2022-04-30 00:51:14 +02:00
David Chavez 7ff34271e8 Release 0.15.1 2022-04-22 23:11:59 +02:00
David Chavez 4f7a5b02a6 Fix: Bug - repeat mode and queue index event (#16) 2022-04-22 23:11:01 +02:00
David Chavez af803339dc More syntax updates and simplification 2022-04-03 13:16:43 +02:00
David Chavez a5bf6eb1dd Use timeDomain as default audioTimePitchAlgorithm 2022-04-03 12:35:30 +02:00
David Chavez 5e0c27b990 More syntax improvements 2022-04-03 12:24:18 +02:00
David Chavez 6079234942 More syntax updates 2022-04-03 12:13:39 +02:00
David Chavez e74b5ffe4d Syntax improvements 2022-04-03 11:49:23 +02:00
David Chavez 92554a187c Release 0.15.0 2022-04-01 23:54:08 +02:00
David Chavez 473651f357 Support mp3 embedded chapters 2022-04-01 23:47:46 +02:00
David Chavez db2f3e9af7 Remove obsolete code 2022-04-01 23:22:26 +02:00
David Chavez a9f831a258 Fix bug in addItems at index and add tests 2022-04-01 21:18:52 +02:00
David Chavez cc3840d81e Fix next/previous with repeat modes 2022-04-01 20:47:54 +02:00
David Chavez 5307090ea3 Replace deprecated “timedMetadata" KVO 2022-04-01 17:47:57 +02:00
David Chavez bdaee8b18f Extract more information from interruptions 2022-04-01 00:14:47 +02:00
David Chavez 84d359bc4f Update README.md 2022-02-24 09:14:36 +01:00
David Chavez 40ea7ad2f9 Release 0.14.7 2022-02-24 08:49:31 +01:00
David Chavez f2f1c1236c Add tests for new seek improvements 2022-02-24 08:48:54 +01:00
Terkel a75f0d0201 fix: make moveItem public and accessible from outside the class (#9) 2022-02-23 21:40:39 +01:00
Jacob Spizziri 9e4e7f6807 fix(seek): fix an issue causing seek to fail if called immediatly after load (#11) 2022-02-23 21:27:38 +01:00
David Chavez dbd3b03989 Release 0.14.6 2021-11-06 14:38:13 +01:00
David Chavez 7e19604df7 Create LICENSE (#5)
* Create LICENSE

* Update LICENSE
2021-11-06 14:29:06 +01:00
David Chavez 481130dc58 Release 0.14.5 2021-10-25 14:08:31 +02:00
David Chavez 300b34afa3 Do not emit paused state when changing tracks 2021-10-25 14:08:01 +02:00
David Chavez da3af0e9db Release 0.14.4 2021-09-28 10:58:23 +02:00
David Chavez d9eb313c1b Deprecate syncRemoteCommandsWithCommandCenter 2021-09-28 10:57:36 +02:00
David Chavez cca7f68da4 Increase deployment target for Test Target 2021-09-28 10:12:22 +02:00
David Chavez 7ed74b80ec Release 0.14.2 2021-09-28 10:04:01 +02:00
David Chavez 2773e4bfec Trigger skip and jump events only when actually taking action 2021-09-28 09:57:24 +02:00
David Chavez 77dc8f4ff1 Fix flickering elapsed time on a lock screen after pause 2021-09-28 09:41:04 +02:00
David Chavez accdf2c00c Rename exposed SPM package name 2021-09-28 09:31:31 +02:00
David Chavez 542d3a5764 Remove syncRemoteCommandsWithCommandCenter
Removed in favor of a didSet on remoteCommands property
2021-09-28 09:28:14 +02:00
David Chavez 4131e54f3e Create FUNDING.yml 2021-09-28 09:19:46 +02:00
David Chavez 03c4a7310f Release 0.14.2 2021-09-25 00:17:58 +02:00
David Chavez 9d2d2594a1 Replace commands based on diff to avoid iOS 15 issues 2021-09-25 00:17:31 +02:00
David Chavez 4e790876cb Release 0.14.1 2021-09-23 10:51:50 +02:00
David Chavez b19d01bdfc Allow manual resyncing of command center commands 2021-09-23 10:51:21 +02:00
David Chavez 3c8ecb353c Release 0.14.0 2021-09-16 17:50:55 +02:00
David Chavez cafd513468 Raise minimum deployment target to iOS11
Due to breaking change in Swift 5.5 & Xcode 13
2021-09-16 17:50:43 +02:00
David Chavez 7b8a4f318d Add tests for repeat mode 2021-08-19 16:27:41 +02:00
David Chavez acab6473b2 Release 0.13.2 2021-08-12 11:40:50 +02:00
David Chavez 57b6fb08f3 Fix tests 2021-08-12 11:40:21 +02:00
David Chavez 68a15ab3a6 Release 0.13.1 2021-08-12 11:18:08 +02:00
David Chavez 92053a2bd0 Add event for queue index changes 2021-08-12 11:16:56 +02:00
David Chavez aeef676164 Persist rate accross player vs item 2021-08-12 11:16:46 +02:00
David Chavez 9c32a86bfa Rename library 2021-08-11 22:25:48 +02:00
David Chavez ce04a796ee Call super on reset of QueuedAudioPlayer 2021-06-26 13:25:00 +02:00
David Chavez 7370ad05e6 Add support for metadata updates (#3) 2021-05-29 22:54:41 +02:00
David Chavez aedae222b0 Move to Github provided machines 2021-05-29 22:27:47 +02:00
David Chavez b2cb178d21 Fix tests 2021-05-29 17:23:51 +02:00
David Chavez 8e62c63fff Disable allowsExternalPlayback 2021-05-29 17:02:43 +02:00
David Chavez f585d7021c Thread safe mutation of now playing info center 2021-05-29 16:59:14 +02:00
David Chavez 32809366fa Allow pausing to be respected if done while loading 2021-05-29 16:43:16 +02:00
David Chavez a6c67e858d Add QueueManagerDelegate to track queue changes 2021-05-29 16:25:31 +02:00
David Chavez 071d0e8017 Add ability to set a repeat mode for QueuedAudioPlayer 2021-05-29 13:55:00 +02:00
David Chavez 6755694566 Add back the podspec 2021-05-29 13:18:02 +02:00
David Chavez 7f117b0670 Remove Codecov (#2) 2021-05-27 23:07:23 +02:00
David Chavez fffe1e5514 Add SPM Support (#1) 2021-05-27 22:19:49 +02:00
Jørgen Henrichsen b042c3ee6c Bump version 0.11.2. 2019-11-28 18:49:18 +01:00
Jørgen Henrichsen ee5db0c0d5 Merge pull request #87 from biesbjerg/always-emit-ready-event
Fixes a problem where the `ready` event would not be fired when passing `playWhenReady: true`.
2019-11-28 18:47:11 +01:00
Kim Biesbjerg a9509d454f always emit ready event 2019-11-01 10:41:37 +01:00
Jørgen Henrichsen e888c7954a Update Readme. Update podspec.
Bump version to 0.11.1.
2019-08-22 22:55:32 +02:00
Jørgen Henrichsen eea9aee4ec Merge pull request #74 from cbess/patch-1
Seek to initial time
2019-08-22 22:52:36 +02:00
Jørgen Henrichsen f2c95fa48c Merge branch 'master' into patch-1 2019-08-22 22:39:46 +02:00
Jørgen Henrichsen 5c8ac4da6b Update README
Remove incorrect recommendation to enable Remote Notifications.
2019-08-22 21:27:36 +02:00
C. Bess 5369d4f4e8 Seek to initial time
Use initial time operation, even for play when ready
2019-08-12 23:13:36 -05:00
Jørgen Henrichsen 09a7548f3a Update README. Update podspec.
Bump verision to 0.11.0.
2019-07-12 18:13:46 +02:00
Jørgen Henrichsen 565b9a04d4 Merge pull request #71 from dcvz/feature/asset-options
Add ability to set asset init options
2019-07-12 18:12:23 +02:00
David Chavez c2ec751ec0 Address review feedback 2019-07-10 13:56:05 +02:00
David Chavez 2c36c6c239 Add ability to set asset init options 2019-07-06 12:04:49 +02:00
Jørgen Henrichsen 1c4cbf676d Merge pull request #70 from jorgenhenrichsen/more-detaile-player-states
More detailed player states
2019-07-03 18:29:43 +02:00
Jørgen Henrichsen ec9492da17 Add tests for the loading state. 2019-07-03 17:48:28 +02:00
Jørgen Henrichsen 5add7699ff Added an example of how to handle network error.
The example app will now handle a network error, and reload when play is called, after a item failed to load because of a network error.
2019-07-03 16:41:14 +02:00
Jørgen Henrichsen bc1d775875 Reset audio file to spotify preview in example app. 2019-07-03 15:06:50 +02:00
Jørgen Henrichsen 4c1a545e87 Update podspec. Remove swift version file.
Clean up the podspec file and set swift version to 5.0. Remove the .swift-version file as it's use is deprecated.
2019-07-03 14:53:24 +02:00
Jørgen Henrichsen f31b52f81b Make the ready state reachable for items with initial time.
Fixes a problem where the .ready state would not be reached for audio items that had an initial time.
2019-07-03 14:25:43 +02:00
Jørgen Henrichsen 355c729078 Update README. Update podspec.
Bump version to 0.10.0.
2019-07-03 13:22:27 +02:00
Jørgen Henrichsen 719d3c852b Add an activityindicator to the example.
Shows how an activityindicator can be used to indicate loading, when player is loading or buffering.
2019-07-03 13:21:41 +02:00
Jørgen Henrichsen 4f33d7e688 Add a new state buffering.
Introduces an additional state to better describe the current state of the player.
Previously the player would be `idle` until the  item was loaded, then changing to `ready`. When the player started to play it would first enter `loading`, then subsequently `playing`.
Now it will instead immediately enter `loading`, then when the item is loaded it will enter `ready`. When the item then is played it will enter `buffering` and then `playing`.
This should make it easier to update UI accordingly to the players current state.
2019-07-03 11:23:10 +02:00
Jørgen Henrichsen 56d0633df0 Update README. Update podspec.
Bump to version 0.9.3.
2019-07-01 16:23:01 +02:00
Jørgen Henrichsen 825e508ecb Merge pull request #67 from jorgenhenrichsen/async-fixes
Async fixes
2019-07-01 16:14:20 +02:00
Jørgen Henrichsen 76d9dc17af Merge branch 'master' into async-fixes 2019-07-01 15:38:45 +02:00
Jørgen Henrichsen 8ceb0216a0 Merge pull request #65 from dcvz/feature/feedback-commands
Add commands for like, dislike and bookmark
2019-07-01 15:38:33 +02:00
David Chavez db41634631 Fix some missing sets 2019-06-25 10:49:10 +02:00
David Chavez 15843b5fe8 Add commands for like, dislike and bookmark 2019-06-24 23:57:17 +02:00
Jørgen Henrichsen 4241b484b2 Removed commented prints. 2019-06-23 18:19:08 +02:00
Jørgen Henrichsen 92bf0b96b6 Remove cancelloading in the load method.
This was already done in the reset(soft:) method.
Removed the nil check before canceling the pendingAsset load, since the coalescing `?` operator was already used anyway.
2019-06-23 18:13:48 +02:00
Jørgen Henrichsen 457dff7c01 Use weak capturing of the AudioPlayer. Remove initial time test.
Weak capturing of the AudioPlayer is done in the listener callback functions in order to not receive INVOPs in the event the audioPlayer is deallocated.
The initial time test is removed because of the unreliable results it produces.
2019-06-23 18:06:43 +02:00
Jørgen Henrichsen c90e9c4976 Unregister observers in their deinit method. 2019-06-23 16:41:47 +02:00
Jørgen Henrichsen 649bb01e89 Weak self and cancel previous load in load(from:playWhenReady).
Use weak self in the loadValuesAsync callback in the event where this is called after the player has be deintialized.
Cancel loading the pendingAsset before loading a new one.
2019-06-23 16:35:50 +02:00
Jørgen Henrichsen 965f7dbbe1 Update README and podspec.
Bump version to 0.9.2.
2019-06-19 12:42:31 +02:00
Jørgen Henrichsen 536414bd44 Merge pull request #51 from minhtc/async-load-asset
load asset async to prevent freeze UI when streaming audio from internet
2019-06-19 12:39:00 +02:00
HackerMeo b7d5db0a55 Merge pull request #1 from jorgenhenrichsen/minhtc-async-load-asset
Fix test problems and a few other things
2019-06-19 16:07:40 +07:00
Jørgen Henrichsen d0907b16c8 Merge branch 'async-load-asset' into minhtc-async-load-asset 2019-06-18 21:34:24 +02:00
Jørgen Henrichsen 60eb4b4676 Removed the currentTime test.
Removes the test that always fails in the CI env at bitrise.
2019-06-18 11:30:47 +02:00
Jørgen Henrichsen 4ec9e9c2a2 Update currentTime tests.
Use a shorter `timeEventFrequenct` in order to recieve the callback quicker, so the test can pass quicker.
2019-06-18 11:16:04 +02:00
Jørgen Henrichsen b2efdf4d65 Rewrote the current time test.
Checks each time a second elapses, instead of just at state == `playing`.
2019-06-13 22:33:41 +02:00
Jørgen Henrichsen d9badfec9b Do not set automaticallyWaitsToMinimizeStalling to false when an item is loaded. 2019-06-13 21:42:47 +02:00
Jørgen Henrichsen 4d0f29adf6 Increased timeout for expectations. 2019-06-13 21:42:47 +02:00
Jørgen Henrichsen f817bc5840 Wrote AudioPlayerTests using the XCTest framework. 2019-06-13 21:42:47 +02:00
Jørgen Henrichsen 5755b70e1b Rewrote AVPlayerWrapperTests using the XCTest framework. 2019-06-13 21:42:47 +02:00
Jørgen Henrichsen 83ac2af5d6 Set timepitchingalgo on audio ready. Update tests to support async loading. 2019-06-13 21:10:57 +02:00
minhtcx a2f001a968 load asset async to prevent freeze UI when streaming audio from internet 2019-06-13 21:10:56 +02:00
Jørgen Henrichsen 9390064581 Merge pull request #62 from anharismail/master
Logo SwiftAudio
2019-06-13 12:15:45 +02:00
Anhar Ismail 7a45ee9e47 Update README.md 2019-06-13 16:57:58 +07:00
Anhar Ismail 17a4b18333 Add files via upload 2019-06-13 16:56:34 +07:00
Anhar Ismail ad01101d3c Create logomark.png 2019-06-13 16:55:40 +07:00
Jørgen Henrichsen d1bbc94bdd Update Readme. Update podspec.
Bump version to 0.9.1.
2019-06-11 13:48:16 +02:00
Jørgen Henrichsen fb5a8dde6c Merge pull request #61 from minhtc/fix-exclusive-access
Fix crash on modification requires exclusive access
2019-06-11 13:47:03 +02:00
minhtcx 9b71040f43 fix crash on modification requires exclusive access 2019-06-10 12:02:46 +07:00
Jørgen Henrichsen 5e50ea48d6 Merge pull request #60 from jorgenhenrichsen/recover-from-AVPlayer-failed
Recover from AVPlayer failed
2019-05-11 19:41:12 +02:00
Jørgen Henrichsen 92a804e9e4 Update Readme. Update podspec.
Bump version to v0.9.0.
2019-05-11 19:30:59 +02:00
Jørgen Henrichsen af4635d047 Remove AVPlayer parameter from init in both AudioPlayer and AVPlayerWrapper. 2019-05-11 19:29:36 +02:00
Jørgen Henrichsen addebef7f9 Add more detailed description on the fail event. 2019-05-11 18:14:54 +02:00
Jørgen Henrichsen 46022ef0d6 Add event when AVPlayer is recreated.
The AudioSession category will need to be set again when this event is emitted.
2019-05-11 17:56:43 +02:00
Jørgen Henrichsen 386dc5202c Recreate the AVPlayer when loading an item, if the previous item failed. 2019-05-11 17:46:55 +02:00
Jørgen Henrichsen 17166093c2 Correctly set isObserving when stopping the observation. 2019-05-11 17:46:27 +02:00
Jørgen Henrichsen c06b8ce64c Made the player observer not retain the AVPlayer.
They will also stop observing the current AVPlayer if a new one is going to be set.
2019-05-11 17:34:24 +02:00
Minh Tran aab8a2302b Merge branch 'master' into async-load-asset 2019-04-19 12:55:36 +07:00
Jørgen Henrichsen 5c8c83f914 Merge pull request #54 from jorgenhenrichsen/swift5
Swift 5
2019-04-18 17:18:00 +02:00
Jørgen Henrichsen ef2d9f5a90 Update README. Update podspec.
Bump version to 0.8.0.
2019-04-18 17:06:57 +02:00
Jørgen Henrichsen 08ebb473f2 Add a custom case to the TimeEventFrequency enum.
Will fix #52.
2019-04-18 16:53:07 +02:00
Jørgen Henrichsen d32a041159 Updated to recommended project settings. 2019-04-18 16:53:07 +02:00
Jørgen Henrichsen 1057a7fca6 Add new handler for non-frozen enums. 2019-04-18 16:53:07 +02:00
Jørgen Henrichsen cee2be50e2 Removed the AudioPlayerDelegate.
Events must be used instead.
2019-04-18 16:53:07 +02:00
Jørgen Henrichsen 8161c3cf02 Update to Swift 5. 2019-04-18 16:53:07 +02:00
Jørgen Henrichsen bb0f301383 Update dependencies. 2019-04-18 16:53:07 +02:00
Jørgen Henrichsen 9da1347d12 Update issue templates
Added issue templates based on Githubs default templates
2019-04-18 12:45:07 +02:00
minhtcx 4a512c6aa0 load asset async to prevent freeze UI when streaming audio from internet 2019-04-17 17:01:55 +07:00
Jørgen Henrichsen f8026d0915 Update podspec. Update README.
Bump versions to 0.7.2.
2019-04-16 09:15:15 +02:00
Jørgen Henrichsen 9d6534f2c9 Merge pull request #49 from dcvz/master
Add `buferredPosition` property
2019-04-16 09:14:10 +02:00
David Chavez c55c25686c Add buferredPosition property 2019-04-14 23:55:44 +02:00
Jørgen Henrichsen 852578c914 Merge pull request #47 from jorgenhenrichsen/queue-access
Queue access
2019-04-07 21:47:09 +02:00
Jørgen Henrichsen 0ae0427556 Update README. Update podspec.
Bump versions to 0.7.1.
2019-04-07 21:40:38 +02:00
Jørgen Henrichsen 9170be5e36 More information available in the QueuedAudioPlayer.
Can get the currentIndex and all items in the queue.
2019-04-07 20:58:27 +02:00
236 changed files with 5439 additions and 18587 deletions
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: DoubleSymmetry
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+32
View File
@@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
If needed, include code examples for the problem or steps to reproduce.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Version [e.g. 0.7.0]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6, Simulator iPhone6 ...]
- OS: [e.g. iOS8.1]
- Version [e.g. 0.7.0]
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution or feature you would like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+27
View File
@@ -0,0 +1,27 @@
name: validate
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
unit-tests:
runs-on: macos-latest
strategy:
matrix:
destination:
[
'platform=iOS Simulator,name=iPhone 12 Pro',
]
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Run Tests
run: |-
cd Example
xcodebuild test -scheme SwiftAudio-Example -destination "${destination}" -enableCodeCoverage YES
env:
destination: ${{ matrix.destination }}
-1
View File
@@ -1 +0,0 @@
4.2
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
-14
View File
@@ -1,14 +0,0 @@
platform :ios, '10.0'
use_frameworks!
target 'SwiftAudio_Example' do
pod 'SwiftAudio', :path => '../'
target 'SwiftAudio_Tests' do
inherit! :search_paths
pod 'Quick', '~> 1.3.0'
pod 'Nimble' , '~> 7.3.0'
end
end
-27
View File
@@ -1,27 +0,0 @@
PODS:
- Nimble (7.3.1)
- Quick (1.3.2)
- SwiftAudio (0.3.3)
DEPENDENCIES:
- Nimble (~> 7.3.0)
- Quick (~> 1.3.0)
- SwiftAudio (from `../`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- Nimble
- Quick
EXTERNAL SOURCES:
SwiftAudio:
:path: "../"
SPEC CHECKSUMS:
Nimble: 04f732da099ea4d153122aec8c2a88fd0c7219ae
Quick: 2623cb30d7a7f41ca62f684f679586558f483d46
SwiftAudio: 2e712c3e04cf172d05639d7bb1516db7afd195da
PODFILE CHECKSUM: 8a75946cbc65d8d98176f80a88d8363a28d118ce
COCOAPODS: 1.5.3
-22
View File
@@ -1,22 +0,0 @@
{
"name": "SwiftAudio",
"version": "0.3.3",
"summary": "Easy audio streaming for iOS",
"description": "SwiftAudio is an audio player written in Swift, making it simpler to work with audio playback from streams and files.",
"homepage": "https://github.com/jorgenhenrichsen/SwiftAudio",
"license": {
"type": "MIT",
"file": "LICENSE"
},
"authors": {
"Jørgen Henrichsen": "jh.henrichs@gmail.com"
},
"source": {
"git": "https://github.com/jorgenhenrichsen/SwiftAudio.git",
"tag": "0.3.3"
},
"platforms": {
"ios": "10.0"
},
"source_files": "SwiftAudio/Classes/**/*"
}
-27
View File
@@ -1,27 +0,0 @@
PODS:
- Nimble (7.3.1)
- Quick (1.3.2)
- SwiftAudio (0.3.3)
DEPENDENCIES:
- Nimble (~> 7.3.0)
- Quick (~> 1.3.0)
- SwiftAudio (from `../`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- Nimble
- Quick
EXTERNAL SOURCES:
SwiftAudio:
:path: "../"
SPEC CHECKSUMS:
Nimble: 04f732da099ea4d153122aec8c2a88fd0c7219ae
Quick: 2623cb30d7a7f41ca62f684f679586558f483d46
SwiftAudio: 2e712c3e04cf172d05639d7bb1516db7afd195da
PODFILE CHECKSUM: 8a75946cbc65d8d98176f80a88d8363a28d118ce
COCOAPODS: 1.5.3
@@ -1,35 +0,0 @@
//
// CwlCatchException.swift
// CwlAssertionTesting
//
// Created by Matt Gallagher on 2016/01/10.
// Copyright © 2016 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
import Foundation
#if SWIFT_PACKAGE
import CwlCatchExceptionSupport
#endif
private func catchReturnTypeConverter<T: NSException>(_ type: T.Type, block: () -> Void) -> T? {
return catchExceptionOfKind(type, block) as? T
}
extension NSException {
public static func catchException(in block: () -> Void) -> Self? {
return catchReturnTypeConverter(self, block: block)
}
}
@@ -1,37 +0,0 @@
//
// CwlCatchException.m
// CwlAssertionTesting
//
// Created by Matt Gallagher on 2016/01/10.
// Copyright © 2016 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#import "CwlCatchException.h"
#if !SWIFT_PACKAGE && NON_SWIFT_PACKAGE
__attribute__((visibility("hidden")))
#endif
NSException* catchExceptionOfKind(Class __nonnull type, __attribute__((noescape)) void (^ __nonnull inBlock)(void)) {
@try {
inBlock();
} @catch (NSException *exception) {
if ([exception isKindOfClass:type]) {
return exception;
} else {
@throw;
}
}
return nil;
}
@@ -1,32 +0,0 @@
//
// CwlCatchException.h
// CwlCatchException
//
// Created by Matt Gallagher on 2016/01/10.
// Copyright © 2016 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#import <Foundation/Foundation.h>
//! Project version number for CwlCatchException.
FOUNDATION_EXPORT double CwlCatchExceptionVersionNumber;
//! Project version string for CwlCatchException.
FOUNDATION_EXPORT const unsigned char CwlCatchExceptionVersionString[];
#if !SWIFT_PACKAGE && NON_SWIFT_PACKAGE
__attribute__((visibility("hidden")))
#endif
NSException* __nullable catchExceptionOfKind(Class __nonnull type, __attribute__((noescape)) void (^ __nonnull inBlock)(void));
@@ -1,50 +0,0 @@
//
// CwlMachBadExceptionHandler.m
// CwlPreconditionTesting
//
// Created by Matt Gallagher on 2016/01/10.
// Copyright © 2016 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#if defined(__x86_64__)
#import "mach_excServer.h"
#import "CwlMachBadInstructionHandler.h"
@protocol BadInstructionReply <NSObject>
+(NSNumber *)receiveReply:(NSValue *)value;
@end
/// A basic function that receives callbacks from mach_exc_server and relays them to the Swift implemented BadInstructionException.catch_mach_exception_raise_state.
kern_return_t catch_mach_exception_raise_state(mach_port_t exception_port, exception_type_t exception, const mach_exception_data_t code, mach_msg_type_number_t codeCnt, int *flavor, const thread_state_t old_state, mach_msg_type_number_t old_stateCnt, thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) {
bad_instruction_exception_reply_t reply = { exception_port, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt };
Class badInstructionClass = NSClassFromString(@"BadInstructionException");
NSValue *value = [NSValue valueWithBytes: &reply objCType: @encode(bad_instruction_exception_reply_t)];
return [[badInstructionClass performSelector: @selector(receiveReply:) withObject: value] intValue];
}
// The mach port should be configured so that this function is never used.
kern_return_t catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt) {
assert(false);
return KERN_FAILURE;
}
// The mach port should be configured so that this function is never used.
kern_return_t catch_mach_exception_raise_state_identity(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state, mach_msg_type_number_t old_stateCnt, thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) {
assert(false);
return KERN_FAILURE;
}
#endif
@@ -1,70 +0,0 @@
//
// CwlMachBadInstructionHandler.h
// CwlPreconditionTesting
//
// Created by Matt Gallagher on 2016/01/10.
// Copyright © 2016 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#import <Foundation/Foundation.h>
#import <mach/mach.h>
NS_ASSUME_NONNULL_BEGIN
extern boolean_t mach_exc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
// The request_mach_exception_raise_t struct is passed to mach_msg which assumes its exact layout. To avoid problems with different layouts, we keep the definition in C rather than Swift.
typedef struct
{
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[224];
} request_mach_exception_raise_t;
// The reply_mach_exception_raise_state_t struct is passed to mach_msg which assumes its exact layout. To avoid problems with different layouts, we keep the definition in C rather than Swift.
typedef struct
{
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[224];
} reply_mach_exception_raise_state_t;
typedef struct
{
mach_port_t exception_port;
exception_type_t exception;
mach_exception_data_type_t const * _Nullable code;
mach_msg_type_number_t codeCnt;
int32_t * _Nullable flavor;
natural_t const * _Nullable old_state;
mach_msg_type_number_t old_stateCnt;
thread_state_t _Nullable new_state;
mach_msg_type_number_t * _Nullable new_stateCnt;
} bad_instruction_exception_reply_t;
NS_ASSUME_NONNULL_END
@@ -1,537 +0,0 @@
/*
* IDENTIFICATION:
* stub generated Sun Jan 29 19:05:29 2017
* with a MiG generated by bootstrap_cmds-96.20.2
* OPTIONS:
*/
#if defined(__x86_64__)
/* Module mach_exc */
#define __MIG_check__Request__mach_exc_subsystem__ 1
#include "mach_excServer.h"
#ifndef mig_internal
#define mig_internal static __inline__
#endif /* mig_internal */
#ifndef mig_external
#define mig_external
#endif /* mig_external */
#if !defined(__MigTypeCheck) && defined(TypeCheck)
#define __MigTypeCheck TypeCheck /* Legacy setting */
#endif /* !defined(__MigTypeCheck) */
#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_)
#define __MigKernelSpecificCode _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting */
#endif /* !defined(__MigKernelSpecificCode) */
#ifndef LimitCheck
#define LimitCheck 0
#endif /* LimitCheck */
#ifndef min
#define min(a,b) ( ((a) < (b))? (a): (b) )
#endif /* min */
#if !defined(_WALIGN_)
#define _WALIGN_(x) (((x) + 3) & ~3)
#endif /* !defined(_WALIGN_) */
#if !defined(_WALIGNSZ_)
#define _WALIGNSZ_(x) _WALIGN_(sizeof(x))
#endif /* !defined(_WALIGNSZ_) */
#ifndef UseStaticTemplates
#define UseStaticTemplates 0
#endif /* UseStaticTemplates */
#ifndef __DeclareRcvRpc
#define __DeclareRcvRpc(_NUM_, _NAME_)
#endif /* __DeclareRcvRpc */
#ifndef __BeforeRcvRpc
#define __BeforeRcvRpc(_NUM_, _NAME_)
#endif /* __BeforeRcvRpc */
#ifndef __AfterRcvRpc
#define __AfterRcvRpc(_NUM_, _NAME_)
#endif /* __AfterRcvRpc */
#ifndef __DeclareRcvSimple
#define __DeclareRcvSimple(_NUM_, _NAME_)
#endif /* __DeclareRcvSimple */
#ifndef __BeforeRcvSimple
#define __BeforeRcvSimple(_NUM_, _NAME_)
#endif /* __BeforeRcvSimple */
#ifndef __AfterRcvSimple
#define __AfterRcvSimple(_NUM_, _NAME_)
#endif /* __AfterRcvSimple */
#define novalue void
#define msgh_request_port msgh_local_port
#define MACH_MSGH_BITS_REQUEST(bits) MACH_MSGH_BITS_LOCAL(bits)
#define msgh_reply_port msgh_remote_port
#define MACH_MSGH_BITS_REPLY(bits) MACH_MSGH_BITS_REMOTE(bits)
#define MIG_RETURN_ERROR(X, code) {\
((mig_reply_error_t *)X)->RetCode = code;\
((mig_reply_error_t *)X)->NDR = NDR_record;\
return;\
}
/* Forward Declarations */
mig_internal novalue _Xmach_exception_raise
(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
mig_internal novalue _Xmach_exception_raise_state
(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
mig_internal novalue _Xmach_exception_raise_state_identity
(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
#if ( __MigTypeCheck )
#if __MIG_check__Request__mach_exc_subsystem__
#if !defined(__MIG_check__Request__mach_exception_raise_t__defined)
#define __MIG_check__Request__mach_exception_raise_t__defined
mig_internal kern_return_t __MIG_check__Request__mach_exception_raise_t(__attribute__((__unused__)) __Request__mach_exception_raise_t *In0P)
{
typedef __Request__mach_exception_raise_t __Request;
#if __MigTypeCheck
unsigned int msgh_size;
#endif /* __MigTypeCheck */
#if __MigTypeCheck
msgh_size = In0P->Head.msgh_size;
if (!(In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
(In0P->msgh_body.msgh_descriptor_count != 2) ||
(msgh_size < (mach_msg_size_t)(sizeof(__Request) - 16)) || (msgh_size > (mach_msg_size_t)sizeof(__Request)))
return MIG_BAD_ARGUMENTS;
#endif /* __MigTypeCheck */
#if __MigTypeCheck
if (In0P->thread.type != MACH_MSG_PORT_DESCRIPTOR ||
In0P->thread.disposition != 17)
return MIG_TYPE_ERROR;
#endif /* __MigTypeCheck */
#if __MigTypeCheck
if (In0P->task.type != MACH_MSG_PORT_DESCRIPTOR ||
In0P->task.disposition != 17)
return MIG_TYPE_ERROR;
#endif /* __MigTypeCheck */
#if defined(__NDR_convert__int_rep__Request__mach_exception_raise_t__codeCnt__defined)
if (In0P->NDR.int_rep != NDR_record.int_rep)
__NDR_convert__int_rep__Request__mach_exception_raise_t__codeCnt(&In0P->codeCnt, In0P->NDR.int_rep);
#endif /* __NDR_convert__int_rep__Request__mach_exception_raise_t__codeCnt__defined */
#if __MigTypeCheck
if ( In0P->codeCnt > 2 )
return MIG_BAD_ARGUMENTS;
if (((msgh_size - (mach_msg_size_t)(sizeof(__Request) - 16)) / 8 < In0P->codeCnt) ||
(msgh_size != (mach_msg_size_t)(sizeof(__Request) - 16) + (8 * In0P->codeCnt)))
return MIG_BAD_ARGUMENTS;
#endif /* __MigTypeCheck */
return MACH_MSG_SUCCESS;
}
#endif /* !defined(__MIG_check__Request__mach_exception_raise_t__defined) */
#endif /* __MIG_check__Request__mach_exc_subsystem__ */
#endif /* ( __MigTypeCheck ) */
/* Routine mach_exception_raise */
mig_internal novalue _Xmach_exception_raise
(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
{
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
mach_msg_trailer_t trailer;
} Request __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
typedef __Request__mach_exception_raise_t __Request;
typedef __Reply__mach_exception_raise_t Reply __attribute__((unused));
/*
* typedef struct {
* mach_msg_header_t Head;
* NDR_record_t NDR;
* kern_return_t RetCode;
* } mig_reply_error_t;
*/
Request *In0P = (Request *) InHeadP;
Reply *OutP = (Reply *) OutHeadP;
#ifdef __MIG_check__Request__mach_exception_raise_t__defined
kern_return_t check_result;
#endif /* __MIG_check__Request__mach_exception_raise_t__defined */
__DeclareRcvRpc(2405, "mach_exception_raise")
__BeforeRcvRpc(2405, "mach_exception_raise")
#if defined(__MIG_check__Request__mach_exception_raise_t__defined)
check_result = __MIG_check__Request__mach_exception_raise_t((__Request *)In0P);
if (check_result != MACH_MSG_SUCCESS)
{ MIG_RETURN_ERROR(OutP, check_result); }
#endif /* defined(__MIG_check__Request__mach_exception_raise_t__defined) */
OutP->RetCode = catch_mach_exception_raise(In0P->Head.msgh_request_port, In0P->thread.name, In0P->task.name, In0P->exception, In0P->code, In0P->codeCnt);
OutP->NDR = NDR_record;
__AfterRcvRpc(2405, "mach_exception_raise")
}
#if ( __MigTypeCheck )
#if __MIG_check__Request__mach_exc_subsystem__
#if !defined(__MIG_check__Request__mach_exception_raise_state_t__defined)
#define __MIG_check__Request__mach_exception_raise_state_t__defined
mig_internal kern_return_t __MIG_check__Request__mach_exception_raise_state_t(__attribute__((__unused__)) __Request__mach_exception_raise_state_t *In0P, __attribute__((__unused__)) __Request__mach_exception_raise_state_t **In1PP)
{
typedef __Request__mach_exception_raise_state_t __Request;
__Request *In1P;
#if __MigTypeCheck
unsigned int msgh_size;
#endif /* __MigTypeCheck */
unsigned int msgh_size_delta;
#if __MigTypeCheck
msgh_size = In0P->Head.msgh_size;
if ((In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
(msgh_size < (mach_msg_size_t)(sizeof(__Request) - 912)) || (msgh_size > (mach_msg_size_t)sizeof(__Request)))
return MIG_BAD_ARGUMENTS;
#endif /* __MigTypeCheck */
#if defined(__NDR_convert__int_rep__Request__mach_exception_raise_state_t__codeCnt__defined)
if (In0P->NDR.int_rep != NDR_record.int_rep)
__NDR_convert__int_rep__Request__mach_exception_raise_state_t__codeCnt(&In0P->codeCnt, In0P->NDR.int_rep);
#endif /* __NDR_convert__int_rep__Request__mach_exception_raise_state_t__codeCnt__defined */
msgh_size_delta = (8 * In0P->codeCnt);
#if __MigTypeCheck
if ( In0P->codeCnt > 2 )
return MIG_BAD_ARGUMENTS;
if (((msgh_size - (mach_msg_size_t)(sizeof(__Request) - 912)) / 8 < In0P->codeCnt) ||
(msgh_size < (mach_msg_size_t)(sizeof(__Request) - 912) + (8 * In0P->codeCnt)))
return MIG_BAD_ARGUMENTS;
msgh_size -= msgh_size_delta;
#endif /* __MigTypeCheck */
*In1PP = In1P = (__Request *) ((pointer_t) In0P + msgh_size_delta - 16);
#if defined(__NDR_convert__int_rep__Request__mach_exception_raise_state_t__old_stateCnt__defined)
if (In0P->NDR.int_rep != NDR_record.int_rep)
__NDR_convert__int_rep__Request__mach_exception_raise_state_t__old_stateCnt(&In1P->old_stateCnt, In1P->NDR.int_rep);
#endif /* __NDR_convert__int_rep__Request__mach_exception_raise_state_t__old_stateCnt__defined */
#if __MigTypeCheck
if ( In1P->old_stateCnt > 224 )
return MIG_BAD_ARGUMENTS;
if (((msgh_size - (mach_msg_size_t)(sizeof(__Request) - 912)) / 4 < In1P->old_stateCnt) ||
(msgh_size != (mach_msg_size_t)(sizeof(__Request) - 912) + (4 * In1P->old_stateCnt)))
return MIG_BAD_ARGUMENTS;
#endif /* __MigTypeCheck */
return MACH_MSG_SUCCESS;
}
#endif /* !defined(__MIG_check__Request__mach_exception_raise_state_t__defined) */
#endif /* __MIG_check__Request__mach_exc_subsystem__ */
#endif /* ( __MigTypeCheck ) */
/* Routine mach_exception_raise_state */
mig_internal novalue _Xmach_exception_raise_state
(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
{
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[224];
mach_msg_trailer_t trailer;
} Request __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
typedef __Request__mach_exception_raise_state_t __Request;
typedef __Reply__mach_exception_raise_state_t Reply __attribute__((unused));
/*
* typedef struct {
* mach_msg_header_t Head;
* NDR_record_t NDR;
* kern_return_t RetCode;
* } mig_reply_error_t;
*/
Request *In0P = (Request *) InHeadP;
Request *In1P;
Reply *OutP = (Reply *) OutHeadP;
#ifdef __MIG_check__Request__mach_exception_raise_state_t__defined
kern_return_t check_result;
#endif /* __MIG_check__Request__mach_exception_raise_state_t__defined */
__DeclareRcvRpc(2406, "mach_exception_raise_state")
__BeforeRcvRpc(2406, "mach_exception_raise_state")
#if defined(__MIG_check__Request__mach_exception_raise_state_t__defined)
check_result = __MIG_check__Request__mach_exception_raise_state_t((__Request *)In0P, (__Request **)&In1P);
if (check_result != MACH_MSG_SUCCESS)
{ MIG_RETURN_ERROR(OutP, check_result); }
#endif /* defined(__MIG_check__Request__mach_exception_raise_state_t__defined) */
OutP->new_stateCnt = 224;
OutP->RetCode = catch_mach_exception_raise_state(In0P->Head.msgh_request_port, In0P->exception, In0P->code, In0P->codeCnt, &In1P->flavor, In1P->old_state, In1P->old_stateCnt, OutP->new_state, &OutP->new_stateCnt);
if (OutP->RetCode != KERN_SUCCESS) {
MIG_RETURN_ERROR(OutP, OutP->RetCode);
}
OutP->NDR = NDR_record;
OutP->flavor = In1P->flavor;
OutP->Head.msgh_size = (mach_msg_size_t)(sizeof(Reply) - 896) + (((4 * OutP->new_stateCnt)));
__AfterRcvRpc(2406, "mach_exception_raise_state")
}
#if ( __MigTypeCheck )
#if __MIG_check__Request__mach_exc_subsystem__
#if !defined(__MIG_check__Request__mach_exception_raise_state_identity_t__defined)
#define __MIG_check__Request__mach_exception_raise_state_identity_t__defined
mig_internal kern_return_t __MIG_check__Request__mach_exception_raise_state_identity_t(__attribute__((__unused__)) __Request__mach_exception_raise_state_identity_t *In0P, __attribute__((__unused__)) __Request__mach_exception_raise_state_identity_t **In1PP)
{
typedef __Request__mach_exception_raise_state_identity_t __Request;
__Request *In1P;
#if __MigTypeCheck
unsigned int msgh_size;
#endif /* __MigTypeCheck */
unsigned int msgh_size_delta;
#if __MigTypeCheck
msgh_size = In0P->Head.msgh_size;
if (!(In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
(In0P->msgh_body.msgh_descriptor_count != 2) ||
(msgh_size < (mach_msg_size_t)(sizeof(__Request) - 912)) || (msgh_size > (mach_msg_size_t)sizeof(__Request)))
return MIG_BAD_ARGUMENTS;
#endif /* __MigTypeCheck */
#if __MigTypeCheck
if (In0P->thread.type != MACH_MSG_PORT_DESCRIPTOR ||
In0P->thread.disposition != 17)
return MIG_TYPE_ERROR;
#endif /* __MigTypeCheck */
#if __MigTypeCheck
if (In0P->task.type != MACH_MSG_PORT_DESCRIPTOR ||
In0P->task.disposition != 17)
return MIG_TYPE_ERROR;
#endif /* __MigTypeCheck */
#if defined(__NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__codeCnt__defined)
if (In0P->NDR.int_rep != NDR_record.int_rep)
__NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__codeCnt(&In0P->codeCnt, In0P->NDR.int_rep);
#endif /* __NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__codeCnt__defined */
msgh_size_delta = (8 * In0P->codeCnt);
#if __MigTypeCheck
if ( In0P->codeCnt > 2 )
return MIG_BAD_ARGUMENTS;
if (((msgh_size - (mach_msg_size_t)(sizeof(__Request) - 912)) / 8 < In0P->codeCnt) ||
(msgh_size < (mach_msg_size_t)(sizeof(__Request) - 912) + (8 * In0P->codeCnt)))
return MIG_BAD_ARGUMENTS;
msgh_size -= msgh_size_delta;
#endif /* __MigTypeCheck */
*In1PP = In1P = (__Request *) ((pointer_t) In0P + msgh_size_delta - 16);
#if defined(__NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__old_stateCnt__defined)
if (In0P->NDR.int_rep != NDR_record.int_rep)
__NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__old_stateCnt(&In1P->old_stateCnt, In1P->NDR.int_rep);
#endif /* __NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__old_stateCnt__defined */
#if __MigTypeCheck
if ( In1P->old_stateCnt > 224 )
return MIG_BAD_ARGUMENTS;
if (((msgh_size - (mach_msg_size_t)(sizeof(__Request) - 912)) / 4 < In1P->old_stateCnt) ||
(msgh_size != (mach_msg_size_t)(sizeof(__Request) - 912) + (4 * In1P->old_stateCnt)))
return MIG_BAD_ARGUMENTS;
#endif /* __MigTypeCheck */
return MACH_MSG_SUCCESS;
}
#endif /* !defined(__MIG_check__Request__mach_exception_raise_state_identity_t__defined) */
#endif /* __MIG_check__Request__mach_exc_subsystem__ */
#endif /* ( __MigTypeCheck ) */
/* Routine mach_exception_raise_state_identity */
mig_internal novalue _Xmach_exception_raise_state_identity
(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
{
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[224];
mach_msg_trailer_t trailer;
} Request __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
typedef __Request__mach_exception_raise_state_identity_t __Request;
typedef __Reply__mach_exception_raise_state_identity_t Reply __attribute__((unused));
/*
* typedef struct {
* mach_msg_header_t Head;
* NDR_record_t NDR;
* kern_return_t RetCode;
* } mig_reply_error_t;
*/
Request *In0P = (Request *) InHeadP;
Request *In1P;
Reply *OutP = (Reply *) OutHeadP;
#ifdef __MIG_check__Request__mach_exception_raise_state_identity_t__defined
kern_return_t check_result;
#endif /* __MIG_check__Request__mach_exception_raise_state_identity_t__defined */
__DeclareRcvRpc(2407, "mach_exception_raise_state_identity")
__BeforeRcvRpc(2407, "mach_exception_raise_state_identity")
#if defined(__MIG_check__Request__mach_exception_raise_state_identity_t__defined)
check_result = __MIG_check__Request__mach_exception_raise_state_identity_t((__Request *)In0P, (__Request **)&In1P);
if (check_result != MACH_MSG_SUCCESS)
{ MIG_RETURN_ERROR(OutP, check_result); }
#endif /* defined(__MIG_check__Request__mach_exception_raise_state_identity_t__defined) */
OutP->new_stateCnt = 224;
OutP->RetCode = catch_mach_exception_raise_state_identity(In0P->Head.msgh_request_port, In0P->thread.name, In0P->task.name, In0P->exception, In0P->code, In0P->codeCnt, &In1P->flavor, In1P->old_state, In1P->old_stateCnt, OutP->new_state, &OutP->new_stateCnt);
if (OutP->RetCode != KERN_SUCCESS) {
MIG_RETURN_ERROR(OutP, OutP->RetCode);
}
OutP->NDR = NDR_record;
OutP->flavor = In1P->flavor;
OutP->Head.msgh_size = (mach_msg_size_t)(sizeof(Reply) - 896) + (((4 * OutP->new_stateCnt)));
__AfterRcvRpc(2407, "mach_exception_raise_state_identity")
}
/* Description of this subsystem, for use in direct RPC */
const struct catch_mach_exc_subsystem catch_mach_exc_subsystem = {
mach_exc_server_routine,
2405,
2408,
(mach_msg_size_t)sizeof(union __ReplyUnion__catch_mach_exc_subsystem),
(vm_address_t)0,
{
{ (mig_impl_routine_t) 0,
(mig_stub_routine_t) _Xmach_exception_raise, 6, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__mach_exception_raise_t)},
{ (mig_impl_routine_t) 0,
(mig_stub_routine_t) _Xmach_exception_raise_state, 9, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__mach_exception_raise_state_t)},
{ (mig_impl_routine_t) 0,
(mig_stub_routine_t) _Xmach_exception_raise_state_identity, 11, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__mach_exception_raise_state_identity_t)},
}
};
mig_external boolean_t mach_exc_server
(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
{
/*
* typedef struct {
* mach_msg_header_t Head;
* NDR_record_t NDR;
* kern_return_t RetCode;
* } mig_reply_error_t;
*/
register mig_routine_t routine;
OutHeadP->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REPLY(InHeadP->msgh_bits), 0);
OutHeadP->msgh_remote_port = InHeadP->msgh_reply_port;
/* Minimal size: routine() will update it if different */
OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
OutHeadP->msgh_local_port = MACH_PORT_NULL;
OutHeadP->msgh_id = InHeadP->msgh_id + 100;
OutHeadP->msgh_reserved = 0;
if ((InHeadP->msgh_id > 2407) || (InHeadP->msgh_id < 2405) ||
((routine = catch_mach_exc_subsystem.routine[InHeadP->msgh_id - 2405].stub_routine) == 0)) {
((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
return FALSE;
}
(*routine) (InHeadP, OutHeadP);
return TRUE;
}
mig_external mig_routine_t mach_exc_server_routine
(mach_msg_header_t *InHeadP)
{
register int msgh_id;
msgh_id = InHeadP->msgh_id - 2405;
if ((msgh_id > 2) || (msgh_id < 0))
return 0;
return catch_mach_exc_subsystem.routine[msgh_id].stub_routine;
}
#endif
@@ -1,321 +0,0 @@
#ifndef _mach_exc_server_
#define _mach_exc_server_
/* Module mach_exc */
#include <string.h>
#include <mach/ndr.h>
#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/notify.h>
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/mig_errors.h>
#include <mach/port.h>
/* BEGIN VOUCHER CODE */
#ifndef KERNEL
#if defined(__has_include)
#if __has_include(<mach/mig_voucher_support.h>)
#ifndef USING_VOUCHERS
#define USING_VOUCHERS
#endif
#ifndef __VOUCHER_FORWARD_TYPE_DECLS__
#define __VOUCHER_FORWARD_TYPE_DECLS__
#ifdef __cplusplus
extern "C" {
#endif
extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import));
#ifdef __cplusplus
}
#endif
#endif // __VOUCHER_FORWARD_TYPE_DECLS__
#endif // __has_include(<mach/mach_voucher_types.h>)
#endif // __has_include
#endif // !KERNEL
/* END VOUCHER CODE */
/* BEGIN MIG_STRNCPY_ZEROFILL CODE */
#if defined(__has_include)
#if __has_include(<mach/mig_strncpy_zerofill_support.h>)
#ifndef USING_MIG_STRNCPY_ZEROFILL
#define USING_MIG_STRNCPY_ZEROFILL
#endif
#ifndef __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__
#define __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__
#ifdef __cplusplus
extern "C" {
#endif
extern int mig_strncpy_zerofill(char *dest, const char *src, int len) __attribute__((weak_import));
#ifdef __cplusplus
}
#endif
#endif /* __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ */
#endif /* __has_include(<mach/mig_strncpy_zerofill_support.h>) */
#endif /* __has_include */
/* END MIG_STRNCPY_ZEROFILL CODE */
#ifdef AUTOTEST
#ifndef FUNCTION_PTR_T
#define FUNCTION_PTR_T
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
typedef struct {
char *name;
function_ptr_t function;
} function_table_entry;
typedef function_table_entry *function_table_t;
#endif /* FUNCTION_PTR_T */
#endif /* AUTOTEST */
#ifndef mach_exc_MSG_COUNT
#define mach_exc_MSG_COUNT 3
#endif /* mach_exc_MSG_COUNT */
#include <mach/std_types.h>
#include <mach/mig.h>
#include <mach/mig.h>
#include <mach/mach_types.h>
#ifdef __BeforeMigServerHeader
__BeforeMigServerHeader
#endif /* __BeforeMigServerHeader */
/* Routine mach_exception_raise */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t catch_mach_exception_raise
(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t codeCnt
);
/* Routine mach_exception_raise_state */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t catch_mach_exception_raise_state
(
mach_port_t exception_port,
exception_type_t exception,
const mach_exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
);
/* Routine mach_exception_raise_state_identity */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t catch_mach_exception_raise_state_identity
(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
);
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
boolean_t mach_exc_server(
mach_msg_header_t *InHeadP,
mach_msg_header_t *OutHeadP);
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
mig_routine_t mach_exc_server_routine(
mach_msg_header_t *InHeadP);
/* Description of this subsystem, for use in direct RPC */
extern const struct catch_mach_exc_subsystem {
mig_server_routine_t server; /* Server routine */
mach_msg_id_t start; /* Min routine number */
mach_msg_id_t end; /* Max routine number + 1 */
unsigned int maxsize; /* Max msg size */
vm_address_t reserved; /* Reserved */
struct routine_descriptor /*Array of routine descriptors */
routine[3];
} catch_mach_exc_subsystem;
/* typedefs for all requests */
#ifndef __Request__mach_exc_subsystem__defined
#define __Request__mach_exc_subsystem__defined
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
} __Request__mach_exception_raise_t __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[224];
} __Request__mach_exception_raise_state_t __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[224];
} __Request__mach_exception_raise_state_identity_t __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
#endif /* !__Request__mach_exc_subsystem__defined */
/* union of all requests */
#ifndef __RequestUnion__catch_mach_exc_subsystem__defined
#define __RequestUnion__catch_mach_exc_subsystem__defined
union __RequestUnion__catch_mach_exc_subsystem {
__Request__mach_exception_raise_t Request_mach_exception_raise;
__Request__mach_exception_raise_state_t Request_mach_exception_raise_state;
__Request__mach_exception_raise_state_identity_t Request_mach_exception_raise_state_identity;
};
#endif /* __RequestUnion__catch_mach_exc_subsystem__defined */
/* typedefs for all replies */
#ifndef __Reply__mach_exc_subsystem__defined
#define __Reply__mach_exc_subsystem__defined
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} __Reply__mach_exception_raise_t __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[224];
} __Reply__mach_exception_raise_state_t __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[224];
} __Reply__mach_exception_raise_state_identity_t __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
#endif /* !__Reply__mach_exc_subsystem__defined */
/* union of all replies */
#ifndef __ReplyUnion__catch_mach_exc_subsystem__defined
#define __ReplyUnion__catch_mach_exc_subsystem__defined
union __ReplyUnion__catch_mach_exc_subsystem {
__Reply__mach_exception_raise_t Reply_mach_exception_raise;
__Reply__mach_exception_raise_state_t Reply_mach_exception_raise_state;
__Reply__mach_exception_raise_state_identity_t Reply_mach_exception_raise_state_identity;
};
#endif /* __RequestUnion__catch_mach_exc_subsystem__defined */
#ifndef subsystem_to_name_map_mach_exc
#define subsystem_to_name_map_mach_exc \
{ "mach_exception_raise", 2405 },\
{ "mach_exception_raise_state", 2406 },\
{ "mach_exception_raise_state_identity", 2407 }
#endif
#ifdef __AfterMigServerHeader
__AfterMigServerHeader
#endif /* __AfterMigServerHeader */
#endif /* _mach_exc_server_ */
@@ -1,89 +0,0 @@
//
// CwlBadInstructionException.swift
// CwlPreconditionTesting
//
// Created by Matt Gallagher on 2016/01/10.
// Copyright © 2016 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
import Foundation
#if SWIFT_PACKAGE
import CwlMachBadInstructionHandler
#endif
private func raiseBadInstructionException() {
BadInstructionException().raise()
}
/// A simple NSException subclass. It's not required to subclass NSException (since the exception type is represented in the name) but this helps for identifying the exception through runtime type.
@objc(BadInstructionException)
public class BadInstructionException: NSException {
static var name: String = "com.cocoawithlove.BadInstruction"
init() {
super.init(name: NSExceptionName(rawValue: BadInstructionException.name), reason: nil, userInfo: nil)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
/// An Objective-C callable function, invoked from the `mach_exc_server` callback function `catch_mach_exception_raise_state` to push the `raiseBadInstructionException` function onto the stack.
@objc(receiveReply:)
public class func receiveReply(_ value: NSValue) -> NSNumber {
#if arch(x86_64)
var reply = bad_instruction_exception_reply_t(exception_port: 0, exception: 0, code: nil, codeCnt: 0, flavor: nil, old_state: nil, old_stateCnt: 0, new_state: nil, new_stateCnt: nil)
withUnsafeMutablePointer(to: &reply) { value.getValue(UnsafeMutableRawPointer($0)) }
let old_state: UnsafePointer<natural_t> = reply.old_state!
let old_stateCnt: mach_msg_type_number_t = reply.old_stateCnt
let new_state: thread_state_t = reply.new_state!
let new_stateCnt: UnsafeMutablePointer<mach_msg_type_number_t> = reply.new_stateCnt!
// Make sure we've been given enough memory
if old_stateCnt != x86_THREAD_STATE64_COUNT || new_stateCnt.pointee < x86_THREAD_STATE64_COUNT {
return NSNumber(value: KERN_INVALID_ARGUMENT)
}
// Read the old thread state
var state = old_state.withMemoryRebound(to: x86_thread_state64_t.self, capacity: 1) { return $0.pointee }
// 1. Decrement the stack pointer
state.__rsp -= __uint64_t(MemoryLayout<Int>.size)
// 2. Save the old Instruction Pointer to the stack.
if let pointer = UnsafeMutablePointer<__uint64_t>(bitPattern: UInt(state.__rsp)) {
pointer.pointee = state.__rip
} else {
return NSNumber(value: KERN_INVALID_ARGUMENT)
}
// 3. Set the Instruction Pointer to the new function's address
var f: @convention(c) () -> Void = raiseBadInstructionException
withUnsafePointer(to: &f) {
state.__rip = $0.withMemoryRebound(to: __uint64_t.self, capacity: 1) { return $0.pointee }
}
// Write the new thread state
new_state.withMemoryRebound(to: x86_thread_state64_t.self, capacity: 1) { $0.pointee = state }
new_stateCnt.pointee = x86_THREAD_STATE64_COUNT
return NSNumber(value: KERN_SUCCESS)
#else
fatalError("Unavailable for this CPU architecture")
#endif
}
}
@@ -1,197 +0,0 @@
//
// CwlCatchBadInstruction.swift
// CwlPreconditionTesting
//
// Created by Matt Gallagher on 2016/01/10.
// Copyright © 2016 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
import Foundation
#if SWIFT_PACKAGE
import CwlMachBadInstructionHandler
#endif
#if arch(x86_64)
private enum PthreadError: Error { case code(Int32) }
private enum MachExcServer: Error { case code(kern_return_t) }
/// A quick function for converting Mach error results into Swift errors
private func kernCheck(_ f: () -> Int32) throws {
let r = f()
guard r == KERN_SUCCESS else {
throw NSError(domain: NSMachErrorDomain, code: Int(r), userInfo: nil)
}
}
extension request_mach_exception_raise_t {
mutating func withMsgHeaderPointer<R>(in block: (UnsafeMutablePointer<mach_msg_header_t>) -> R) -> R {
return withUnsafeMutablePointer(to: &self) { p -> R in
return p.withMemoryRebound(to: mach_msg_header_t.self, capacity: 1) { ptr -> R in
return block(ptr)
}
}
}
}
extension reply_mach_exception_raise_state_t {
mutating func withMsgHeaderPointer<R>(in block: (UnsafeMutablePointer<mach_msg_header_t>) -> R) -> R {
return withUnsafeMutablePointer(to: &self) { p -> R in
return p.withMemoryRebound(to: mach_msg_header_t.self, capacity: 1) { ptr -> R in
return block(ptr)
}
}
}
}
/// A structure used to store context associated with the Mach message port
private struct MachContext {
var masks = execTypesCountTuple<exception_mask_t>()
var count: mach_msg_type_number_t = 0
var ports = execTypesCountTuple<mach_port_t>()
var behaviors = execTypesCountTuple<exception_behavior_t>()
var flavors = execTypesCountTuple<thread_state_flavor_t>()
var currentExceptionPort: mach_port_t = 0
var handlerThread: pthread_t? = nil
static func internalMutablePointers<R>(_ m: UnsafeMutablePointer<execTypesCountTuple<exception_mask_t>>, _ c: UnsafeMutablePointer<mach_msg_type_number_t>, _ p: UnsafeMutablePointer<execTypesCountTuple<mach_port_t>>, _ b: UnsafeMutablePointer<execTypesCountTuple<exception_behavior_t>>, _ f: UnsafeMutablePointer<execTypesCountTuple<thread_state_flavor_t>>, _ block: (UnsafeMutablePointer<exception_mask_t>, UnsafeMutablePointer<mach_msg_type_number_t>, UnsafeMutablePointer<mach_port_t>, UnsafeMutablePointer<exception_behavior_t>, UnsafeMutablePointer<thread_state_flavor_t>) -> R) -> R {
return m.withMemoryRebound(to: exception_mask_t.self, capacity: 1) { masksPtr in
return c.withMemoryRebound(to: mach_msg_type_number_t.self, capacity: 1) { countPtr in
return p.withMemoryRebound(to: mach_port_t.self, capacity: 1) { portsPtr in
return b.withMemoryRebound(to: exception_behavior_t.self, capacity: 1) { behaviorsPtr in
return f.withMemoryRebound(to: thread_state_flavor_t.self, capacity: 1) { flavorsPtr in
return block(masksPtr, countPtr, portsPtr, behaviorsPtr, flavorsPtr)
}
}
}
}
}
}
mutating func withUnsafeMutablePointers<R>(in block: @escaping (UnsafeMutablePointer<exception_mask_t>, UnsafeMutablePointer<mach_msg_type_number_t>, UnsafeMutablePointer<mach_port_t>, UnsafeMutablePointer<exception_behavior_t>, UnsafeMutablePointer<thread_state_flavor_t>) -> R) -> R {
return MachContext.internalMutablePointers(&masks, &count, &ports, &behaviors, &flavors, block)
}
}
/// A function for receiving mach messages and parsing the first with mach_exc_server (and if any others are received, throwing them away).
private func machMessageHandler(_ arg: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? {
let context = arg.assumingMemoryBound(to: MachContext.self).pointee
var request = request_mach_exception_raise_t()
var reply = reply_mach_exception_raise_state_t()
var handledfirstException = false
repeat { do {
// Request the next mach message from the port
request.Head.msgh_local_port = context.currentExceptionPort
request.Head.msgh_size = UInt32(MemoryLayout<request_mach_exception_raise_t>.size)
let requestSize = request.Head.msgh_size
try kernCheck { request.withMsgHeaderPointer { requestPtr in
mach_msg(requestPtr, MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0, requestSize, context.currentExceptionPort, 0, UInt32(MACH_PORT_NULL))
} }
// Prepare the reply structure
reply.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request.Head.msgh_bits), 0)
reply.Head.msgh_local_port = UInt32(MACH_PORT_NULL)
reply.Head.msgh_remote_port = request.Head.msgh_remote_port
reply.Head.msgh_size = UInt32(MemoryLayout<reply_mach_exception_raise_state_t>.size)
reply.NDR = NDR_record
if !handledfirstException {
// Use the MiG generated server to invoke our handler for the request and fill in the rest of the reply structure
guard request.withMsgHeaderPointer(in: { requestPtr in reply.withMsgHeaderPointer { replyPtr in
mach_exc_server(requestPtr, replyPtr)
} }) != 0 else { throw MachExcServer.code(reply.RetCode) }
handledfirstException = true
} else {
// If multiple fatal errors occur, don't handle subsquent errors (let the program crash)
reply.RetCode = KERN_FAILURE
}
// Send the reply
let replySize = reply.Head.msgh_size
try kernCheck { reply.withMsgHeaderPointer { replyPtr in
mach_msg(replyPtr, MACH_SEND_MSG, replySize, 0, UInt32(MACH_PORT_NULL), 0, UInt32(MACH_PORT_NULL))
} }
} catch let error as NSError where (error.domain == NSMachErrorDomain && (error.code == Int(MACH_RCV_PORT_CHANGED) || error.code == Int(MACH_RCV_INVALID_NAME))) {
// Port was already closed before we started or closed while we were listening.
// This means the controlling thread shut down.
return nil
} catch {
// Should never be reached but this is testing code, don't try to recover, just abort
fatalError("Mach message error: \(error)")
} } while true
}
/// Run the provided block. If a mach "BAD_INSTRUCTION" exception is raised, catch it and return a BadInstructionException (which captures stack information about the throw site, if desired). Otherwise return nil.
/// NOTE: This function is only intended for use in test harnesses use in a distributed build is almost certainly a bad choice. If a "BAD_INSTRUCTION" exception is raised, the block will be exited before completion via Objective-C exception. The risks associated with an Objective-C exception apply here: most Swift/Objective-C functions are *not* exception-safe. Memory may be leaked and the program will not necessarily be left in a safe state.
/// - parameter block: a function without parameters that will be run
/// - returns: if an EXC_BAD_INSTRUCTION is raised during the execution of `block` then a BadInstructionException will be returned, otherwise `nil`.
public func catchBadInstruction(in block: () -> Void) -> BadInstructionException? {
var context = MachContext()
var result: BadInstructionException? = nil
do {
var handlerThread: pthread_t? = nil
defer {
// 8. Wait for the thread to terminate *if* we actually made it to the creation point
// The mach port should be destroyed *before* calling pthread_join to avoid a deadlock.
if handlerThread != nil {
pthread_join(handlerThread!, nil)
}
}
try kernCheck {
// 1. Create the mach port
mach_port_allocate(mach_task_self_, MACH_PORT_RIGHT_RECEIVE, &context.currentExceptionPort)
}
defer {
// 7. Cleanup the mach port
mach_port_destroy(mach_task_self_, context.currentExceptionPort)
}
try kernCheck {
// 2. Configure the mach port
mach_port_insert_right(mach_task_self_, context.currentExceptionPort, context.currentExceptionPort, MACH_MSG_TYPE_MAKE_SEND)
}
let currentExceptionPtr = context.currentExceptionPort
try kernCheck { context.withUnsafeMutablePointers { masksPtr, countPtr, portsPtr, behaviorsPtr, flavorsPtr in
// 3. Apply the mach port as the handler for this thread
thread_swap_exception_ports(mach_thread_self(), EXC_MASK_BAD_INSTRUCTION, currentExceptionPtr, Int32(bitPattern: UInt32(EXCEPTION_STATE) | MACH_EXCEPTION_CODES), x86_THREAD_STATE64, masksPtr, countPtr, portsPtr, behaviorsPtr, flavorsPtr)
} }
defer { context.withUnsafeMutablePointers { masksPtr, countPtr, portsPtr, behaviorsPtr, flavorsPtr in
// 6. Unapply the mach port
_ = thread_swap_exception_ports(mach_thread_self(), EXC_MASK_BAD_INSTRUCTION, 0, EXCEPTION_DEFAULT, THREAD_STATE_NONE, masksPtr, countPtr, portsPtr, behaviorsPtr, flavorsPtr)
} }
try withUnsafeMutablePointer(to: &context) { c throws in
// 4. Create the thread
let e = pthread_create(&handlerThread, nil, machMessageHandler, c)
guard e == 0 else { throw PthreadError.code(e) }
// 5. Run the block
result = BadInstructionException.catchException(in: block)
}
} catch {
// Should never be reached but this is testing code, don't try to recover, just abort
fatalError("Mach port error: \(error)")
}
return result
}
#endif
@@ -1,55 +0,0 @@
//
// CwlDarwinDefinitions.swift
// CwlPreconditionTesting
//
// Created by Matt Gallagher on 2016/01/10.
// Copyright © 2016 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
import Darwin
#if arch(x86_64)
// From /usr/include/mach/message.h
// #define MACH_MSG_TYPE_MAKE_SEND 20 /* Must hold receive right */
// #define MACH_MSGH_BITS_REMOTE(bits) \
// ((bits) & MACH_MSGH_BITS_REMOTE_MASK)
// #define MACH_MSGH_BITS(remote, local) /* legacy */ \
// ((remote) | ((local) << 8))
public let MACH_MSG_TYPE_MAKE_SEND: UInt32 = 20
public func MACH_MSGH_BITS_REMOTE(_ bits: UInt32) -> UInt32 { return bits & UInt32(MACH_MSGH_BITS_REMOTE_MASK) }
public func MACH_MSGH_BITS(_ remote: UInt32, _ local: UInt32) -> UInt32 { return ((remote) | ((local) << 8)) }
// From /usr/include/mach/exception_types.h
// #define EXC_BAD_INSTRUCTION 2 /* Instruction failed */
// #define EXC_MASK_BAD_INSTRUCTION (1 << EXC_BAD_INSTRUCTION)
public let EXC_BAD_INSTRUCTION: UInt32 = 2
public let EXC_MASK_BAD_INSTRUCTION: UInt32 = 1 << EXC_BAD_INSTRUCTION
// From /usr/include/mach/i386/thread_status.h
// #define x86_THREAD_STATE64_COUNT ((mach_msg_type_number_t) \
// ( sizeof (x86_thread_state64_t) / sizeof (int) ))
public let x86_THREAD_STATE64_COUNT = UInt32(MemoryLayout<x86_thread_state64_t>.size / MemoryLayout<Int32>.size)
public let EXC_TYPES_COUNT = 14
public struct execTypesCountTuple<T: ExpressibleByIntegerLiteral> {
// From /usr/include/mach/i386/exception.h
// #define EXC_TYPES_COUNT 14 /* incl. illegal exception 0 */
public var value: (T, T, T, T, T, T, T, T, T, T, T, T, T, T) = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
public init() {
}
}
#endif
@@ -1,32 +0,0 @@
//
// CwlPreconditionTesting.h
// CwlPreconditionTesting
//
// Created by Matt Gallagher on 2016/01/10.
// Copyright © 2016 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#import <Foundation/Foundation.h>
extern bool _swift_reportFatalErrorsToDebugger;
//! Project version number for CwlUtils.
FOUNDATION_EXPORT double CwlPreconditionTestingVersionNumber;
//! Project version string for CwlUtils.
FOUNDATION_EXPORT const unsigned char CwlAssertingTestingVersionString[];
#include "CwlMachBadInstructionHandler.h"
#include "CwlCatchException.h"
-201
View File
@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2016 Quick Team
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-1764
View File
File diff suppressed because it is too large Load Diff
@@ -1,17 +0,0 @@
import Foundation
/// Protocol for the assertion handler that Nimble uses for all expectations.
public protocol AssertionHandler {
func assert(_ assertion: Bool, message: FailureMessage, location: SourceLocation)
}
/// Global backing interface for assertions that Nimble creates.
/// Defaults to a private test handler that passes through to XCTest.
///
/// If XCTest is not available, you must assign your own assertion handler
/// before using any matchers, otherwise Nimble will abort the program.
///
/// @see AssertionHandler
public var NimbleAssertionHandler: AssertionHandler = { () -> AssertionHandler in
return isXCTestAvailable() ? NimbleXCTestHandler() : NimbleXCTestUnavailableHandler()
}()
@@ -1,19 +0,0 @@
/// AssertionDispatcher allows multiple AssertionHandlers to receive
/// assertion messages.
///
/// @warning Does not fully dispatch if one of the handlers raises an exception.
/// This is possible with XCTest-based assertion handlers.
///
public class AssertionDispatcher: AssertionHandler {
let handlers: [AssertionHandler]
public init(handlers: [AssertionHandler]) {
self.handlers = handlers
}
public func assert(_ assertion: Bool, message: FailureMessage, location: SourceLocation) {
for handler in handlers {
handler.assert(assertion, message: message, location: location)
}
}
}
@@ -1,100 +0,0 @@
import Foundation
/// A data structure that stores information about an assertion when
/// AssertionRecorder is set as the Nimble assertion handler.
///
/// @see AssertionRecorder
/// @see AssertionHandler
public struct AssertionRecord: CustomStringConvertible {
/// Whether the assertion succeeded or failed
public let success: Bool
/// The failure message the assertion would display on failure.
public let message: FailureMessage
/// The source location the expectation occurred on.
public let location: SourceLocation
public var description: String {
return "AssertionRecord { success=\(success), message='\(message.stringValue)', location=\(location) }"
}
}
/// An AssertionHandler that silently records assertions that Nimble makes.
/// This is useful for testing failure messages for matchers.
///
/// @see AssertionHandler
public class AssertionRecorder: AssertionHandler {
/// All the assertions that were captured by this recorder
public var assertions = [AssertionRecord]()
public init() {}
public func assert(_ assertion: Bool, message: FailureMessage, location: SourceLocation) {
assertions.append(
AssertionRecord(
success: assertion,
message: message,
location: location))
}
}
/// Allows you to temporarily replace the current Nimble assertion handler with
/// the one provided for the scope of the closure.
///
/// Once the closure finishes, then the original Nimble assertion handler is restored.
///
/// @see AssertionHandler
public func withAssertionHandler(_ tempAssertionHandler: AssertionHandler, closure: () throws -> Void) {
let environment = NimbleEnvironment.activeInstance
let oldRecorder = environment.assertionHandler
let capturer = NMBExceptionCapture(handler: nil, finally: ({
environment.assertionHandler = oldRecorder
}))
environment.assertionHandler = tempAssertionHandler
capturer.tryBlock {
try! closure()
}
}
/// Captures expectations that occur in the given closure. Note that all
/// expectations will still go through to the default Nimble handler.
///
/// This can be useful if you want to gather information about expectations
/// that occur within a closure.
///
/// @param silently expectations are no longer send to the default Nimble
/// assertion handler when this is true. Defaults to false.
///
/// @see gatherFailingExpectations
public func gatherExpectations(silently: Bool = false, closure: () -> Void) -> [AssertionRecord] {
let previousRecorder = NimbleEnvironment.activeInstance.assertionHandler
let recorder = AssertionRecorder()
let handlers: [AssertionHandler]
if silently {
handlers = [recorder]
} else {
handlers = [recorder, previousRecorder]
}
let dispatcher = AssertionDispatcher(handlers: handlers)
withAssertionHandler(dispatcher, closure: closure)
return recorder.assertions
}
/// Captures failed expectations that occur in the given closure. Note that all
/// expectations will still go through to the default Nimble handler.
///
/// This can be useful if you want to gather information about failed
/// expectations that occur within a closure.
///
/// @param silently expectations are no longer send to the default Nimble
/// assertion handler when this is true. Defaults to false.
///
/// @see gatherExpectations
/// @see raiseException source for an example use case.
public func gatherFailingExpectations(silently: Bool = false, closure: () -> Void) -> [AssertionRecord] {
let assertions = gatherExpectations(silently: silently, closure: closure)
return assertions.filter { assertion in
!assertion.success
}
}
@@ -1,187 +0,0 @@
import Foundation
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
private func from(objcPredicate: NMBPredicate) -> Predicate<NSObject> {
return Predicate { actualExpression in
let result = objcPredicate.satisfies(({ try actualExpression.evaluate() }),
location: actualExpression.location)
return result.toSwift()
}
}
internal struct ObjCMatcherWrapper: Matcher {
let matcher: NMBMatcher
func matches(_ actualExpression: Expression<NSObject>, failureMessage: FailureMessage) -> Bool {
return matcher.matches(
({ try! actualExpression.evaluate() }),
failureMessage: failureMessage,
location: actualExpression.location)
}
func doesNotMatch(_ actualExpression: Expression<NSObject>, failureMessage: FailureMessage) -> Bool {
return matcher.doesNotMatch(
({ try! actualExpression.evaluate() }),
failureMessage: failureMessage,
location: actualExpression.location)
}
}
// Equivalent to Expectation, but for Nimble's Objective-C interface
public class NMBExpectation: NSObject {
internal let _actualBlock: () -> NSObject?
internal var _negative: Bool
internal let _file: FileString
internal let _line: UInt
internal var _timeout: TimeInterval = 1.0
@objc public init(actualBlock: @escaping () -> NSObject?, negative: Bool, file: FileString, line: UInt) {
self._actualBlock = actualBlock
self._negative = negative
self._file = file
self._line = line
}
private var expectValue: Expectation<NSObject> {
return expect(_file, line: _line) {
self._actualBlock() as NSObject?
}
}
@objc public var withTimeout: (TimeInterval) -> NMBExpectation {
return ({ timeout in self._timeout = timeout
return self
})
}
@objc public var to: (NMBMatcher) -> Void {
return ({ matcher in
if let pred = matcher as? NMBPredicate {
self.expectValue.to(from(objcPredicate: pred))
} else {
self.expectValue.to(ObjCMatcherWrapper(matcher: matcher))
}
})
}
@objc public var toWithDescription: (NMBMatcher, String) -> Void {
return ({ matcher, description in
if let pred = matcher as? NMBPredicate {
self.expectValue.to(from(objcPredicate: pred), description: description)
} else {
self.expectValue.to(ObjCMatcherWrapper(matcher: matcher), description: description)
}
})
}
@objc public var toNot: (NMBMatcher) -> Void {
return ({ matcher in
if let pred = matcher as? NMBPredicate {
self.expectValue.toNot(from(objcPredicate: pred))
} else {
self.expectValue.toNot(ObjCMatcherWrapper(matcher: matcher))
}
})
}
@objc public var toNotWithDescription: (NMBMatcher, String) -> Void {
return ({ matcher, description in
if let pred = matcher as? NMBPredicate {
self.expectValue.toNot(from(objcPredicate: pred), description: description)
} else {
self.expectValue.toNot(ObjCMatcherWrapper(matcher: matcher), description: description)
}
})
}
@objc public var notTo: (NMBMatcher) -> Void { return toNot }
@objc public var notToWithDescription: (NMBMatcher, String) -> Void { return toNotWithDescription }
@objc public var toEventually: (NMBMatcher) -> Void {
return ({ matcher in
if let pred = matcher as? NMBPredicate {
self.expectValue.toEventually(
from(objcPredicate: pred),
timeout: self._timeout,
description: nil
)
} else {
self.expectValue.toEventually(
ObjCMatcherWrapper(matcher: matcher),
timeout: self._timeout,
description: nil
)
}
})
}
@objc public var toEventuallyWithDescription: (NMBMatcher, String) -> Void {
return ({ matcher, description in
if let pred = matcher as? NMBPredicate {
self.expectValue.toEventually(
from(objcPredicate: pred),
timeout: self._timeout,
description: description
)
} else {
self.expectValue.toEventually(
ObjCMatcherWrapper(matcher: matcher),
timeout: self._timeout,
description: description
)
}
})
}
@objc public var toEventuallyNot: (NMBMatcher) -> Void {
return ({ matcher in
if let pred = matcher as? NMBPredicate {
self.expectValue.toEventuallyNot(
from(objcPredicate: pred),
timeout: self._timeout,
description: nil
)
} else {
self.expectValue.toEventuallyNot(
ObjCMatcherWrapper(matcher: matcher),
timeout: self._timeout,
description: nil
)
}
})
}
@objc public var toEventuallyNotWithDescription: (NMBMatcher, String) -> Void {
return ({ matcher, description in
if let pred = matcher as? NMBPredicate {
self.expectValue.toEventuallyNot(
from(objcPredicate: pred),
timeout: self._timeout,
description: description
)
} else {
self.expectValue.toEventuallyNot(
ObjCMatcherWrapper(matcher: matcher),
timeout: self._timeout,
description: description
)
}
})
}
@objc public var toNotEventually: (NMBMatcher) -> Void {
return toEventuallyNot
}
@objc public var toNotEventuallyWithDescription: (NMBMatcher, String) -> Void {
return toEventuallyNotWithDescription
}
@objc public class func failWithMessage(_ message: String, file: FileString, line: UInt) {
fail(message, location: SourceLocation(file: file, line: line))
}
}
#endif
@@ -1,93 +0,0 @@
import Foundation
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
// swiftlint:disable line_length
public typealias MatcherBlock = (_ actualExpression: Expression<NSObject>, _ failureMessage: FailureMessage) throws -> Bool
public typealias FullMatcherBlock = (_ actualExpression: Expression<NSObject>, _ failureMessage: FailureMessage, _ shouldNotMatch: Bool) throws -> Bool
// swiftlint:enable line_length
public class NMBObjCMatcher: NSObject, NMBMatcher {
let _match: MatcherBlock
let _doesNotMatch: MatcherBlock
let canMatchNil: Bool
public init(canMatchNil: Bool, matcher: @escaping MatcherBlock, notMatcher: @escaping MatcherBlock) {
self.canMatchNil = canMatchNil
self._match = matcher
self._doesNotMatch = notMatcher
}
public convenience init(matcher: @escaping MatcherBlock) {
self.init(canMatchNil: true, matcher: matcher)
}
public convenience init(canMatchNil: Bool, matcher: @escaping MatcherBlock) {
self.init(canMatchNil: canMatchNil, matcher: matcher, notMatcher: ({ actualExpression, failureMessage in
return try !matcher(actualExpression, failureMessage)
}))
}
public convenience init(matcher: @escaping FullMatcherBlock) {
self.init(canMatchNil: true, matcher: matcher)
}
public convenience init(canMatchNil: Bool, matcher: @escaping FullMatcherBlock) {
self.init(canMatchNil: canMatchNil, matcher: ({ actualExpression, failureMessage in
return try matcher(actualExpression, failureMessage, false)
}), notMatcher: ({ actualExpression, failureMessage in
return try matcher(actualExpression, failureMessage, true)
}))
}
private func canMatch(_ actualExpression: Expression<NSObject>, failureMessage: FailureMessage) -> Bool {
do {
if !canMatchNil {
if try actualExpression.evaluate() == nil {
failureMessage.postfixActual = " (use beNil() to match nils)"
return false
}
}
} catch let error {
failureMessage.actualValue = "an unexpected error thrown: \(error)"
return false
}
return true
}
public func matches(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
let expr = Expression(expression: actualBlock, location: location)
let result: Bool
do {
result = try _match(expr, failureMessage)
} catch let error {
failureMessage.stringValue = "unexpected error thrown: <\(error)>"
return false
}
if self.canMatch(Expression(expression: actualBlock, location: location), failureMessage: failureMessage) {
return result
} else {
return false
}
}
public func doesNotMatch(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
let expr = Expression(expression: actualBlock, location: location)
let result: Bool
do {
result = try _doesNotMatch(expr, failureMessage)
} catch let error {
failureMessage.stringValue = "unexpected error thrown: <\(error)>"
return false
}
if self.canMatch(Expression(expression: actualBlock, location: location), failureMessage: failureMessage) {
return result
} else {
return false
}
}
}
#endif
@@ -1,45 +0,0 @@
import Dispatch
import Foundation
/// "Global" state of Nimble is stored here. Only DSL functions should access / be aware of this
/// class' existence
internal class NimbleEnvironment {
static var activeInstance: NimbleEnvironment {
get {
let env = Thread.current.threadDictionary["NimbleEnvironment"]
if let env = env as? NimbleEnvironment {
return env
} else {
let newEnv = NimbleEnvironment()
self.activeInstance = newEnv
return newEnv
}
}
set {
Thread.current.threadDictionary["NimbleEnvironment"] = newValue
}
}
// TODO: eventually migrate the global to this environment value
var assertionHandler: AssertionHandler {
get { return NimbleAssertionHandler }
set { NimbleAssertionHandler = newValue }
}
var suppressTVOSAssertionWarning: Bool = false
var awaiter: Awaiter
init() {
let timeoutQueue: DispatchQueue
if #available(OSX 10.10, *) {
timeoutQueue = DispatchQueue.global(qos: .userInitiated)
} else {
timeoutQueue = DispatchQueue.global(priority: .high)
}
awaiter = Awaiter(
waitLock: AssertionWaitLock(),
asyncQueue: .main,
timeoutQueue: timeoutQueue)
}
}
@@ -1,92 +0,0 @@
import Foundation
import XCTest
/// Default handler for Nimble. This assertion handler passes failures along to
/// XCTest.
public class NimbleXCTestHandler: AssertionHandler {
public func assert(_ assertion: Bool, message: FailureMessage, location: SourceLocation) {
if !assertion {
recordFailure("\(message.stringValue)\n", location: location)
}
}
}
/// Alternative handler for Nimble. This assertion handler passes failures along
/// to XCTest by attempting to reduce the failure message size.
public class NimbleShortXCTestHandler: AssertionHandler {
public func assert(_ assertion: Bool, message: FailureMessage, location: SourceLocation) {
if !assertion {
let msg: String
if let actual = message.actualValue {
msg = "got: \(actual) \(message.postfixActual)"
} else {
msg = "expected \(message.to) \(message.postfixMessage)"
}
recordFailure("\(msg)\n", location: location)
}
}
}
/// Fallback handler in case XCTest is unavailable. This assertion handler will abort
/// the program if it is invoked.
class NimbleXCTestUnavailableHandler: AssertionHandler {
func assert(_ assertion: Bool, message: FailureMessage, location: SourceLocation) {
fatalError("XCTest is not available and no custom assertion handler was configured. Aborting.")
}
}
#if !SWIFT_PACKAGE
/// Helper class providing access to the currently executing XCTestCase instance, if any
@objc final internal class CurrentTestCaseTracker: NSObject, XCTestObservation {
@objc static let sharedInstance = CurrentTestCaseTracker()
private(set) var currentTestCase: XCTestCase?
private var stashed_swift_reportFatalErrorsToDebugger: Bool = false
@objc func testCaseWillStart(_ testCase: XCTestCase) {
#if swift(>=3.2)
stashed_swift_reportFatalErrorsToDebugger = _swift_reportFatalErrorsToDebugger
_swift_reportFatalErrorsToDebugger = false
#endif
currentTestCase = testCase
}
@objc func testCaseDidFinish(_ testCase: XCTestCase) {
currentTestCase = nil
#if swift(>=3.2)
_swift_reportFatalErrorsToDebugger = stashed_swift_reportFatalErrorsToDebugger
#endif
}
}
#endif
func isXCTestAvailable() -> Bool {
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
// XCTest is weakly linked and so may not be present
return NSClassFromString("XCTestCase") != nil
#else
return true
#endif
}
public func recordFailure(_ message: String, location: SourceLocation) {
#if SWIFT_PACKAGE
XCTFail("\(message)", file: location.file, line: location.line)
#else
if let testCase = CurrentTestCaseTracker.sharedInstance.currentTestCase {
#if swift(>=4)
let line = Int(location.line)
#else
let line = location.line
#endif
testCase.recordFailure(withDescription: message, inFile: location.file, atLine: line, expected: true)
} else {
let msg = "Attempted to report a test failure to XCTest while no test case was running. " +
"The failure was:\n\"\(message)\"\nIt occurred at: \(location.file):\(location.line)"
NSException(name: .internalInconsistencyException, reason: msg, userInfo: nil).raise()
}
#endif
}
-116
View File
@@ -1,116 +0,0 @@
import Dispatch
import Foundation
private enum ErrorResult {
case exception(NSException)
case error(Error)
case none
}
/// Only classes, protocols, methods, properties, and subscript declarations can be
/// bridges to Objective-C via the @objc keyword. This class encapsulates callback-style
/// asynchronous waiting logic so that it may be called from Objective-C and Swift.
internal class NMBWait: NSObject {
// About these kind of lines, `@objc` attributes are only required for Objective-C
// support, so that should be conditional on Darwin platforms and normal Xcode builds
// (non-SwiftPM builds).
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
@objc
internal class func until(
timeout: TimeInterval,
file: FileString = #file,
line: UInt = #line,
action: @escaping (@escaping () -> Void) -> Void) {
return throwableUntil(timeout: timeout, file: file, line: line) { done in
action(done)
}
}
#else
internal class func until(
timeout: TimeInterval,
file: FileString = #file,
line: UInt = #line,
action: @escaping (@escaping () -> Void) -> Void) {
return throwableUntil(timeout: timeout, file: file, line: line) { done in
action(done)
}
}
#endif
// Using a throwable closure makes this method not objc compatible.
internal class func throwableUntil(
timeout: TimeInterval,
file: FileString = #file,
line: UInt = #line,
action: @escaping (@escaping () -> Void) throws -> Void) {
let awaiter = NimbleEnvironment.activeInstance.awaiter
let leeway = timeout / 2.0
// swiftlint:disable:next line_length
let result = awaiter.performBlock(file: file, line: line) { (done: @escaping (ErrorResult) -> Void) throws -> Void in
DispatchQueue.main.async {
let capture = NMBExceptionCapture(
handler: ({ exception in
done(.exception(exception))
}),
finally: ({ })
)
capture.tryBlock {
do {
try action {
done(.none)
}
} catch let e {
done(.error(e))
}
}
}
}.timeout(timeout, forcefullyAbortTimeout: leeway).wait("waitUntil(...)", file: file, line: line)
switch result {
case .incomplete: internalError("Reached .incomplete state for waitUntil(...).")
case .blockedRunLoop:
fail(blockedRunLoopErrorMessageFor("-waitUntil()", leeway: leeway),
file: file, line: line)
case .timedOut:
let pluralize = (timeout == 1 ? "" : "s")
fail("Waited more than \(timeout) second\(pluralize)", file: file, line: line)
case let .raisedException(exception):
fail("Unexpected exception raised: \(exception)")
case let .errorThrown(error):
fail("Unexpected error thrown: \(error)")
case .completed(.exception(let exception)):
fail("Unexpected exception raised: \(exception)")
case .completed(.error(let error)):
fail("Unexpected error thrown: \(error)")
case .completed(.none): // success
break
}
}
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
@objc(untilFile:line:action:)
internal class func until(_ file: FileString = #file, line: UInt = #line, action: @escaping (() -> Void) -> Void) {
until(timeout: 1, file: file, line: line, action: action)
}
#else
internal class func until(_ file: FileString = #file, line: UInt = #line, action: @escaping (() -> Void) -> Void) {
until(timeout: 1, file: file, line: line, action: action)
}
#endif
}
internal func blockedRunLoopErrorMessageFor(_ fnName: String, leeway: TimeInterval) -> String {
// swiftlint:disable:next line_length
return "\(fnName) timed out but was unable to run the timeout handler because the main thread is unresponsive (\(leeway) seconds is allow after the wait times out). Conditions that may cause this include processing blocking IO on the main thread, calls to sleep(), deadlocks, and synchronous IPC. Nimble forcefully stopped run loop which may cause future failures in test run."
}
/// Wait asynchronously until the done closure is called or the timeout has been reached.
///
/// @discussion
/// Call the done() closure to indicate the waiting has completed.
///
/// This function manages the main run loop (`NSRunLoop.mainRunLoop()`) while this function
/// is executing. Any attempts to touch the run loop may cause non-deterministic behavior.
public func waitUntil(timeout: TimeInterval = AsyncDefaults.Timeout, file: FileString = #file, line: UInt = #line, action: @escaping (@escaping () -> Void) -> Void) {
NMBWait.until(timeout: timeout, file: file, line: line, action: action)
}
-64
View File
@@ -1,64 +0,0 @@
import Foundation
/// Make an expectation on a given actual value. The value given is lazily evaluated.
public func expect<T>(_ expression: @autoclosure @escaping () throws -> T?, file: FileString = #file, line: UInt = #line) -> Expectation<T> {
return Expectation(
expression: Expression(
expression: expression,
location: SourceLocation(file: file, line: line),
isClosure: true))
}
/// Make an expectation on a given actual value. The closure is lazily invoked.
public func expect<T>(_ file: FileString = #file, line: UInt = #line, expression: @escaping () throws -> T?) -> Expectation<T> {
return Expectation(
expression: Expression(
expression: expression,
location: SourceLocation(file: file, line: line),
isClosure: true))
}
/// Always fails the test with a message and a specified location.
public func fail(_ message: String, location: SourceLocation) {
let handler = NimbleEnvironment.activeInstance.assertionHandler
handler.assert(false, message: FailureMessage(stringValue: message), location: location)
}
/// Always fails the test with a message.
public func fail(_ message: String, file: FileString = #file, line: UInt = #line) {
fail(message, location: SourceLocation(file: file, line: line))
}
/// Always fails the test.
public func fail(_ file: FileString = #file, line: UInt = #line) {
fail("fail() always fails", file: file, line: line)
}
/// Like Swift's precondition(), but raises NSExceptions instead of sigaborts
internal func nimblePrecondition(
_ expr: @autoclosure() -> Bool,
_ name: @autoclosure() -> String,
_ message: @autoclosure() -> String,
file: StaticString = #file,
line: UInt = #line) {
let result = expr()
if !result {
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let e = NSException(
name: NSExceptionName(name()),
reason: message(),
userInfo: nil)
e.raise()
#else
preconditionFailure("\(name()) - \(message())", file: file, line: line)
#endif
}
}
internal func internalError(_ msg: String, file: FileString = #file, line: UInt = #line) -> Never {
fatalError(
"Nimble Bug Found: \(msg) at \(file):\(line).\n" +
"Please file a bug to Nimble: https://github.com/Quick/Nimble/issues with the " +
"code snippet that caused this error."
)
}
-125
View File
@@ -1,125 +0,0 @@
import Foundation
// Deprecated
internal func expressionDoesNotMatch<T, U>(_ expression: Expression<T>, matcher: U, toNot: String, description: String?) -> (Bool, FailureMessage)
where U: Matcher, U.ValueType == T {
let msg = FailureMessage()
msg.userDescription = description
msg.to = toNot
do {
let pass = try matcher.doesNotMatch(expression, failureMessage: msg)
if msg.actualValue == "" {
msg.actualValue = "<\(stringify(try expression.evaluate()))>"
}
return (pass, msg)
} catch let error {
msg.stringValue = "unexpected error thrown: <\(error)>"
return (false, msg)
}
}
internal func execute<T>(_ expression: Expression<T>, _ style: ExpectationStyle, _ predicate: Predicate<T>, to: String, description: String?, captureExceptions: Bool = true) -> (Bool, FailureMessage) {
func run() -> (Bool, FailureMessage) {
let msg = FailureMessage()
msg.userDescription = description
msg.to = to
do {
let result = try predicate.satisfies(expression)
result.message.update(failureMessage: msg)
if msg.actualValue == "" {
msg.actualValue = "<\(stringify(try expression.evaluate()))>"
}
return (result.toBoolean(expectation: style), msg)
} catch let error {
msg.stringValue = "unexpected error thrown: <\(error)>"
return (false, msg)
}
}
var result: (Bool, FailureMessage) = (false, FailureMessage())
if captureExceptions {
let capture = NMBExceptionCapture(handler: ({ exception -> Void in
let msg = FailureMessage()
msg.stringValue = "unexpected exception raised: \(exception)"
result = (false, msg)
}), finally: nil)
capture.tryBlock {
result = run()
}
} else {
result = run()
}
return result
}
public struct Expectation<T> {
public let expression: Expression<T>
public init(expression: Expression<T>) {
self.expression = expression
}
public func verify(_ pass: Bool, _ message: FailureMessage) {
let handler = NimbleEnvironment.activeInstance.assertionHandler
handler.assert(pass, message: message, location: expression.location)
}
////////////////// OLD API /////////////////////
/// DEPRECATED: Tests the actual value using a matcher to match.
public func to<U>(_ matcher: U, description: String? = nil)
where U: Matcher, U.ValueType == T {
let (pass, msg) = execute(
expression,
.toMatch,
matcher.predicate,
to: "to",
description: description,
captureExceptions: false
)
verify(pass, msg)
}
/// DEPRECATED: Tests the actual value using a matcher to not match.
public func toNot<U>(_ matcher: U, description: String? = nil)
where U: Matcher, U.ValueType == T {
// swiftlint:disable:next line_length
let (pass, msg) = expressionDoesNotMatch(expression, matcher: matcher, toNot: "to not", description: description)
verify(pass, msg)
}
/// DEPRECATED: Tests the actual value using a matcher to not match.
///
/// Alias to toNot().
public func notTo<U>(_ matcher: U, description: String? = nil)
where U: Matcher, U.ValueType == T {
toNot(matcher, description: description)
}
////////////////// NEW API /////////////////////
/// Tests the actual value using a matcher to match.
public func to(_ predicate: Predicate<T>, description: String? = nil) {
let (pass, msg) = execute(expression, .toMatch, predicate, to: "to", description: description)
verify(pass, msg)
}
/// Tests the actual value using a matcher to not match.
public func toNot(_ predicate: Predicate<T>, description: String? = nil) {
let (pass, msg) = execute(expression, .toNotMatch, predicate, to: "to not", description: description)
verify(pass, msg)
}
/// Tests the actual value using a matcher to not match.
///
/// Alias to toNot().
public func notTo(_ predicate: Predicate<T>, description: String? = nil) {
toNot(predicate, description: description)
}
// see:
// - `async` for extension
// - NMBExpectation for Objective-C interface
}
@@ -1,264 +0,0 @@
import Foundation
public indirect enum ExpectationMessage {
// --- Primary Expectations ---
/// includes actual value in output ("expected to <message>, got <actual>")
case expectedActualValueTo(/* message: */ String)
/// uses a custom actual value string in output ("expected to <message>, got <actual>")
case expectedCustomValueTo(/* message: */ String, /* actual: */ String)
/// excludes actual value in output ("expected to <message>")
case expectedTo(/* message: */ String)
/// allows any free-form message ("<message>")
case fail(/* message: */ String)
// --- Composite Expectations ---
// Generally, you'll want the methods, appended(message:) and appended(details:) instead.
/// Not Fully Implemented Yet.
case prepends(/* Prepended Message */ String, ExpectationMessage)
/// appends after an existing message ("<expectation> (use beNil() to match nils)")
case appends(ExpectationMessage, /* Appended Message */ String)
/// provides long-form multi-line explainations ("<expectation>\n\n<string>")
case details(ExpectationMessage, String)
internal var sampleMessage: String {
let asStr = toString(actual: "<ACTUAL>", expected: "expected", to: "to")
let asFailureMessage = FailureMessage()
update(failureMessage: asFailureMessage)
// swiftlint:disable:next line_length
return "(toString(actual:expected:to:) -> \(asStr) || update(failureMessage:) -> \(asFailureMessage.stringValue))"
}
/// Returns the smallest message after the "expected to" string that summarizes the error.
///
/// Returns the message part from ExpectationMessage, ignoring all .appends and .details.
public var expectedMessage: String {
switch self {
case let .fail(msg):
return msg
case let .expectedTo(msg):
return msg
case let .expectedActualValueTo(msg):
return msg
case let .expectedCustomValueTo(msg, _):
return msg
case let .prepends(_, expectation):
return expectation.expectedMessage
case let .appends(expectation, msg):
return "\(expectation.expectedMessage)\(msg)"
case let .details(expectation, _):
return expectation.expectedMessage
}
}
/// Appends a message after the primary expectation message
public func appended(message: String) -> ExpectationMessage {
switch self {
case .fail, .expectedTo, .expectedActualValueTo, .expectedCustomValueTo, .appends, .prepends:
return .appends(self, message)
case let .details(expectation, msg):
return .details(expectation.appended(message: message), msg)
}
}
/// Appends a message hinting to use beNil() for when the actual value given was nil.
public func appendedBeNilHint() -> ExpectationMessage {
return appended(message: " (use beNil() to match nils)")
}
/// Appends a detailed (aka - multiline) message after the primary expectation message
/// Detailed messages will be placed after .appended(message:) calls.
public func appended(details: String) -> ExpectationMessage {
return .details(self, details)
}
internal func visitLeafs(_ f: (ExpectationMessage) -> ExpectationMessage) -> ExpectationMessage {
switch self {
case .fail, .expectedTo, .expectedActualValueTo, .expectedCustomValueTo:
return f(self)
case let .prepends(msg, expectation):
return .prepends(msg, expectation.visitLeafs(f))
case let .appends(expectation, msg):
return .appends(expectation.visitLeafs(f), msg)
case let .details(expectation, msg):
return .details(expectation.visitLeafs(f), msg)
}
}
/// Replaces a primary expectation with one returned by f. Preserves all composite expectations
/// that were built upon it (aka - all appended(message:) and appended(details:).
public func replacedExpectation(_ f: @escaping (ExpectationMessage) -> ExpectationMessage) -> ExpectationMessage {
func walk(_ msg: ExpectationMessage) -> ExpectationMessage {
switch msg {
case .fail, .expectedTo, .expectedActualValueTo, .expectedCustomValueTo:
return f(msg)
default:
return msg
}
}
return visitLeafs(walk)
}
/// Wraps a primary expectation with text before and after it.
/// Alias to prepended(message: before).appended(message: after)
public func wrappedExpectation(before: String, after: String) -> ExpectationMessage {
return prepended(expectation: before).appended(message: after)
}
/// Prepends a message by modifying the primary expectation
public func prepended(expectation message: String) -> ExpectationMessage {
func walk(_ msg: ExpectationMessage) -> ExpectationMessage {
switch msg {
case let .expectedTo(msg):
return .expectedTo(message + msg)
case let .expectedActualValueTo(msg):
return .expectedActualValueTo(message + msg)
case let .expectedCustomValueTo(msg, actual):
return .expectedCustomValueTo(message + msg, actual)
default:
return msg.visitLeafs(walk)
}
}
return visitLeafs(walk)
}
// TODO: test & verify correct behavior
internal func prepended(message: String) -> ExpectationMessage {
return .prepends(message, self)
}
/// Converts the tree of ExpectationMessages into a final built string.
public func toString(actual: String, expected: String = "expected", to: String = "to") -> String {
switch self {
case let .fail(msg):
return msg
case let .expectedTo(msg):
return "\(expected) \(to) \(msg)"
case let .expectedActualValueTo(msg):
return "\(expected) \(to) \(msg), got \(actual)"
case let .expectedCustomValueTo(msg, actual):
return "\(expected) \(to) \(msg), got \(actual)"
case let .prepends(msg, expectation):
return "\(msg)\(expectation.toString(actual: actual, expected: expected, to: to))"
case let .appends(expectation, msg):
return "\(expectation.toString(actual: actual, expected: expected, to: to))\(msg)"
case let .details(expectation, msg):
return "\(expectation.toString(actual: actual, expected: expected, to: to))\n\n\(msg)"
}
}
// Backwards compatibility: converts ExpectationMessage tree to FailureMessage
internal func update(failureMessage: FailureMessage) {
switch self {
case let .fail(msg) where !msg.isEmpty:
failureMessage.stringValue = msg
case .fail:
break
case let .expectedTo(msg):
failureMessage.actualValue = nil
failureMessage.postfixMessage = msg
case let .expectedActualValueTo(msg):
failureMessage.postfixMessage = msg
case let .expectedCustomValueTo(msg, actual):
failureMessage.postfixMessage = msg
failureMessage.actualValue = actual
case let .prepends(msg, expectation):
expectation.update(failureMessage: failureMessage)
if let desc = failureMessage.userDescription {
failureMessage.userDescription = "\(msg)\(desc)"
} else {
failureMessage.userDescription = msg
}
case let .appends(expectation, msg):
expectation.update(failureMessage: failureMessage)
failureMessage.appendMessage(msg)
case let .details(expectation, msg):
expectation.update(failureMessage: failureMessage)
failureMessage.appendDetails(msg)
}
}
}
extension FailureMessage {
internal func toExpectationMessage() -> ExpectationMessage {
let defaultMsg = FailureMessage()
if expected != defaultMsg.expected || _stringValueOverride != nil {
return .fail(stringValue)
}
var msg: ExpectationMessage = .fail(userDescription ?? "")
if actualValue != "" && actualValue != nil {
msg = .expectedCustomValueTo(postfixMessage, actualValue ?? "")
} else if postfixMessage != defaultMsg.postfixMessage {
if actualValue == nil {
msg = .expectedTo(postfixMessage)
} else {
msg = .expectedActualValueTo(postfixMessage)
}
}
if postfixActual != defaultMsg.postfixActual {
msg = .appends(msg, postfixActual)
}
if let m = extendedMessage {
msg = .details(msg, m)
}
return msg
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
public class NMBExpectationMessage: NSObject {
private let msg: ExpectationMessage
internal init(swift msg: ExpectationMessage) {
self.msg = msg
}
public init(expectedTo message: String) {
self.msg = .expectedTo(message)
}
public init(expectedActualValueTo message: String) {
self.msg = .expectedActualValueTo(message)
}
public init(expectedActualValueTo message: String, customActualValue actual: String) {
self.msg = .expectedCustomValueTo(message, actual)
}
public init(fail message: String) {
self.msg = .fail(message)
}
public init(prepend message: String, child: NMBExpectationMessage) {
self.msg = .prepends(message, child.msg)
}
public init(appendedMessage message: String, child: NMBExpectationMessage) {
self.msg = .appends(child.msg, message)
}
public init(prependedMessage message: String, child: NMBExpectationMessage) {
self.msg = .prepends(message, child.msg)
}
public init(details message: String, child: NMBExpectationMessage) {
self.msg = .details(child.msg, message)
}
public func appendedBeNilHint() -> NMBExpectationMessage {
return NMBExpectationMessage(swift: msg.appendedBeNilHint())
}
public func toSwift() -> ExpectationMessage { return self.msg }
}
extension ExpectationMessage {
func toObjectiveC() -> NMBExpectationMessage {
return NMBExpectationMessage(swift: self)
}
}
#endif
-99
View File
@@ -1,99 +0,0 @@
import Foundation
// Memoizes the given closure, only calling the passed
// closure once; even if repeat calls to the returned closure
internal func memoizedClosure<T>(_ closure: @escaping () throws -> T) -> (Bool) throws -> T {
var cache: T?
return ({ withoutCaching in
if withoutCaching || cache == nil {
cache = try closure()
}
return cache!
})
}
/// Expression represents the closure of the value inside expect(...).
/// Expressions are memoized by default. This makes them safe to call
/// evaluate() multiple times without causing a re-evaluation of the underlying
/// closure.
///
/// @warning Since the closure can be any code, Objective-C code may choose
/// to raise an exception. Currently, Expression does not memoize
/// exception raising.
///
/// This provides a common consumable API for matchers to utilize to allow
/// Nimble to change internals to how the captured closure is managed.
public struct Expression<T> {
internal let _expression: (Bool) throws -> T?
internal let _withoutCaching: Bool
public let location: SourceLocation
public let isClosure: Bool
/// Creates a new expression struct. Normally, expect(...) will manage this
/// creation process. The expression is memoized.
///
/// @param expression The closure that produces a given value.
/// @param location The source location that this closure originates from.
/// @param isClosure A bool indicating if the captured expression is a
/// closure or internally produced closure. Some matchers
/// may require closures. For example, toEventually()
/// requires an explicit closure. This gives Nimble
/// flexibility if @autoclosure behavior changes between
/// Swift versions. Nimble internals always sets this true.
public init(expression: @escaping () throws -> T?, location: SourceLocation, isClosure: Bool = true) {
self._expression = memoizedClosure(expression)
self.location = location
self._withoutCaching = false
self.isClosure = isClosure
}
/// Creates a new expression struct. Normally, expect(...) will manage this
/// creation process.
///
/// @param expression The closure that produces a given value.
/// @param location The source location that this closure originates from.
/// @param withoutCaching Indicates if the struct should memoize the given
/// closure's result. Subsequent evaluate() calls will
/// not call the given closure if this is true.
/// @param isClosure A bool indicating if the captured expression is a
/// closure or internally produced closure. Some matchers
/// may require closures. For example, toEventually()
/// requires an explicit closure. This gives Nimble
/// flexibility if @autoclosure behavior changes between
/// Swift versions. Nimble internals always sets this true.
public init(memoizedExpression: @escaping (Bool) throws -> T?, location: SourceLocation, withoutCaching: Bool, isClosure: Bool = true) {
self._expression = memoizedExpression
self.location = location
self._withoutCaching = withoutCaching
self.isClosure = isClosure
}
/// Returns a new Expression from the given expression. Identical to a map()
/// on this type. This should be used only to typecast the Expression's
/// closure value.
///
/// The returned expression will preserve location and isClosure.
///
/// @param block The block that can cast the current Expression value to a
/// new type.
public func cast<U>(_ block: @escaping (T?) throws -> U?) -> Expression<U> {
return Expression<U>(
expression: ({ try block(self.evaluate()) }),
location: self.location,
isClosure: self.isClosure
)
}
public func evaluate() throws -> T? {
return try self._expression(_withoutCaching)
}
public func withoutCaching() -> Expression<T> {
return Expression(
memoizedExpression: self._expression,
location: location,
withoutCaching: true,
isClosure: isClosure
)
}
}
-92
View File
@@ -1,92 +0,0 @@
import Foundation
/// Encapsulates the failure message that matchers can report to the end user.
///
/// This is shared state between Nimble and matchers that mutate this value.
public class FailureMessage: NSObject {
public var expected: String = "expected"
public var actualValue: String? = "" // empty string -> use default; nil -> exclude
public var to: String = "to"
public var postfixMessage: String = "match"
public var postfixActual: String = ""
/// An optional message that will be appended as a new line and provides additional details
/// about the failure. This message will only be visible in the issue navigator / in logs but
/// not directly in the source editor since only a single line is presented there.
public var extendedMessage: String?
public var userDescription: String?
public var stringValue: String {
get {
if let value = _stringValueOverride {
return value
} else {
return computeStringValue()
}
}
set {
_stringValueOverride = newValue
}
}
internal var _stringValueOverride: String?
internal var hasOverriddenStringValue: Bool {
return _stringValueOverride != nil
}
public override init() {
}
public init(stringValue: String) {
_stringValueOverride = stringValue
}
internal func stripNewlines(_ str: String) -> String {
let whitespaces = CharacterSet.whitespacesAndNewlines
return str
.components(separatedBy: "\n")
.map { line in line.trimmingCharacters(in: whitespaces) }
.joined(separator: "")
}
internal func computeStringValue() -> String {
var value = "\(expected) \(to) \(postfixMessage)"
if let actualValue = actualValue {
value = "\(expected) \(to) \(postfixMessage), got \(actualValue)\(postfixActual)"
}
value = stripNewlines(value)
if let extendedMessage = extendedMessage {
value += "\n\(stripNewlines(extendedMessage))"
}
if let userDescription = userDescription {
return "\(userDescription)\n\(value)"
}
return value
}
internal func appendMessage(_ msg: String) {
if hasOverriddenStringValue {
stringValue += "\(msg)"
} else if actualValue != nil {
postfixActual += msg
} else {
postfixMessage += msg
}
}
internal func appendDetails(_ msg: String) {
if hasOverriddenStringValue {
if let desc = userDescription {
stringValue = "\(desc)\n\(stringValue)"
}
stringValue += "\n\(msg)"
} else {
if let desc = userDescription {
userDescription = desc
}
extendedMessage = msg
}
}
}
-121
View File
@@ -1,121 +0,0 @@
import Foundation
public func allPass<T, U>
(_ passFunc: @escaping (T?) throws -> Bool) -> Predicate<U>
where U: Sequence, T == U.Iterator.Element {
let matcher = Predicate.simpleNilable("pass a condition") { actualExpression in
return PredicateStatus(bool: try passFunc(try actualExpression.evaluate()))
}
return createPredicate(matcher)
}
public func allPass<T, U>
(_ passName: String, _ passFunc: @escaping (T?) throws -> Bool) -> Predicate<U>
where U: Sequence, T == U.Iterator.Element {
let matcher = Predicate.simpleNilable(passName) { actualExpression in
return PredicateStatus(bool: try passFunc(try actualExpression.evaluate()))
}
return createPredicate(matcher)
}
public func allPass<S, M>(_ elementMatcher: M) -> Predicate<S>
where S: Sequence, M: Matcher, S.Iterator.Element == M.ValueType {
return createPredicate(elementMatcher.predicate)
}
public func allPass<S>(_ elementPredicate: Predicate<S.Iterator.Element>) -> Predicate<S>
where S: Sequence {
return createPredicate(elementPredicate)
}
private func createPredicate<S>(_ elementMatcher: Predicate<S.Iterator.Element>) -> Predicate<S>
where S: Sequence {
return Predicate { actualExpression in
guard let actualValue = try actualExpression.evaluate() else {
return PredicateResult(
status: .fail,
message: .appends(.expectedTo("all pass"), " (use beNil() to match nils)")
)
}
var failure: ExpectationMessage = .expectedTo("all pass")
for currentElement in actualValue {
let exp = Expression(
expression: {currentElement}, location: actualExpression.location)
let predicateResult = try elementMatcher.satisfies(exp)
if predicateResult.status == .matches {
failure = predicateResult.message.prepended(expectation: "all ")
} else {
failure = predicateResult.message
.replacedExpectation({ .expectedTo($0.expectedMessage) })
.wrappedExpectation(
before: "all ",
after: ", but failed first at element <\(stringify(currentElement))>"
+ " in <\(stringify(actualValue))>"
)
return PredicateResult(status: .doesNotMatch, message: failure)
}
}
failure = failure.replacedExpectation({ expectation in
return .expectedTo(expectation.expectedMessage)
})
return PredicateResult(status: .matches, message: failure)
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func allPassMatcher(_ matcher: NMBMatcher) -> NMBPredicate {
return NMBPredicate { actualExpression in
let location = actualExpression.location
let actualValue = try actualExpression.evaluate()
var nsObjects = [NSObject]()
var collectionIsUsable = true
if let value = actualValue as? NSFastEnumeration {
var generator = NSFastEnumerationIterator(value)
while let obj = generator.next() {
if let nsObject = obj as? NSObject {
nsObjects.append(nsObject)
} else {
collectionIsUsable = false
break
}
}
} else {
collectionIsUsable = false
}
if !collectionIsUsable {
return NMBPredicateResult(
status: NMBPredicateStatus.fail,
message: NMBExpectationMessage(
// swiftlint:disable:next line_length
fail: "allPass can only be used with types which implement NSFastEnumeration (NSArray, NSSet, ...), and whose elements subclass NSObject, got <\(actualValue?.description ?? "nil")>"
)
)
}
let expr = Expression(expression: ({ nsObjects }), location: location)
let pred: Predicate<[NSObject]> = createPredicate(Predicate { expr in
if let predicate = matcher as? NMBPredicate {
return predicate.satisfies(({ try expr.evaluate() }), location: expr.location).toSwift()
} else {
let failureMessage = FailureMessage()
let result = matcher.matches(
({ try! expr.evaluate() }),
failureMessage: failureMessage,
location: expr.location
)
let expectationMsg = failureMessage.toExpectationMessage()
return PredicateResult(
bool: result,
message: expectationMsg
)
}
})
return try pred.satisfies(expr).toObjectiveC()
}
}
}
#endif
-175
View File
@@ -1,175 +0,0 @@
import Foundation
/// If you are running on a slower machine, it could be useful to increase the default timeout value
/// or slow down poll interval. Default timeout interval is 1, and poll interval is 0.01.
public struct AsyncDefaults {
public static var Timeout: TimeInterval = 1
public static var PollInterval: TimeInterval = 0.01
}
private func async<T>(style: ExpectationStyle, predicate: Predicate<T>, timeout: TimeInterval, poll: TimeInterval, fnName: String) -> Predicate<T> {
return Predicate { actualExpression in
let uncachedExpression = actualExpression.withoutCaching()
let fnName = "expect(...).\(fnName)(...)"
var lastPredicateResult: PredicateResult?
let result = pollBlock(
pollInterval: poll,
timeoutInterval: timeout,
file: actualExpression.location.file,
line: actualExpression.location.line,
fnName: fnName) {
lastPredicateResult = try predicate.satisfies(uncachedExpression)
return lastPredicateResult!.toBoolean(expectation: style)
}
switch result {
case .completed: return lastPredicateResult!
case .timedOut: return PredicateResult(status: .fail, message: lastPredicateResult!.message)
case let .errorThrown(error):
return PredicateResult(status: .fail, message: .fail("unexpected error thrown: <\(error)>"))
case let .raisedException(exception):
return PredicateResult(status: .fail, message: .fail("unexpected exception raised: \(exception)"))
case .blockedRunLoop:
// swiftlint:disable:next line_length
return PredicateResult(status: .fail, message: lastPredicateResult!.message.appended(message: " (timed out, but main thread was unresponsive)."))
case .incomplete:
internalError("Reached .incomplete state for \(fnName)(...).")
}
}
}
private let toEventuallyRequiresClosureError = FailureMessage(
// swiftlint:disable:next line_length
stringValue: "expect(...).toEventually(...) requires an explicit closure (eg - expect { ... }.toEventually(...) )\nSwift 1.2 @autoclosure behavior has changed in an incompatible way for Nimble to function"
)
extension Expectation {
/// Tests the actual value using a matcher to match by checking continuously
/// at each pollInterval until the timeout is reached.
///
/// @discussion
/// This function manages the main run loop (`NSRunLoop.mainRunLoop()`) while this function
/// is executing. Any attempts to touch the run loop may cause non-deterministic behavior.
public func toEventually(_ predicate: Predicate<T>, timeout: TimeInterval = AsyncDefaults.Timeout, pollInterval: TimeInterval = AsyncDefaults.PollInterval, description: String? = nil) {
nimblePrecondition(expression.isClosure, "NimbleInternalError", toEventuallyRequiresClosureError.stringValue)
let (pass, msg) = execute(
expression,
.toMatch,
async(style: .toMatch, predicate: predicate, timeout: timeout, poll: pollInterval, fnName: "toEventually"),
to: "to eventually",
description: description,
captureExceptions: false
)
verify(pass, msg)
}
/// Tests the actual value using a matcher to not match by checking
/// continuously at each pollInterval until the timeout is reached.
///
/// @discussion
/// This function manages the main run loop (`NSRunLoop.mainRunLoop()`) while this function
/// is executing. Any attempts to touch the run loop may cause non-deterministic behavior.
public func toEventuallyNot(_ predicate: Predicate<T>, timeout: TimeInterval = AsyncDefaults.Timeout, pollInterval: TimeInterval = AsyncDefaults.PollInterval, description: String? = nil) {
nimblePrecondition(expression.isClosure, "NimbleInternalError", toEventuallyRequiresClosureError.stringValue)
let (pass, msg) = execute(
expression,
.toNotMatch,
async(
style: .toNotMatch,
predicate: predicate,
timeout: timeout,
poll: pollInterval,
fnName: "toEventuallyNot"
),
to: "to eventually not",
description: description,
captureExceptions: false
)
verify(pass, msg)
}
/// Tests the actual value using a matcher to not match by checking
/// continuously at each pollInterval until the timeout is reached.
///
/// Alias of toEventuallyNot()
///
/// @discussion
/// This function manages the main run loop (`NSRunLoop.mainRunLoop()`) while this function
/// is executing. Any attempts to touch the run loop may cause non-deterministic behavior.
public func toNotEventually(_ predicate: Predicate<T>, timeout: TimeInterval = AsyncDefaults.Timeout, pollInterval: TimeInterval = AsyncDefaults.PollInterval, description: String? = nil) {
return toEventuallyNot(predicate, timeout: timeout, pollInterval: pollInterval, description: description)
}
}
// Deprecated
extension Expectation {
/// Tests the actual value using a matcher to match by checking continuously
/// at each pollInterval until the timeout is reached.
///
/// @discussion
/// This function manages the main run loop (`NSRunLoop.mainRunLoop()`) while this function
/// is executing. Any attempts to touch the run loop may cause non-deterministic behavior.
public func toEventually<U>(_ matcher: U, timeout: TimeInterval = AsyncDefaults.Timeout, pollInterval: TimeInterval = AsyncDefaults.PollInterval, description: String? = nil)
where U: Matcher, U.ValueType == T {
if expression.isClosure {
let (pass, msg) = execute(
expression,
.toMatch,
async(
style: .toMatch,
predicate: matcher.predicate,
timeout: timeout,
poll: pollInterval,
fnName: "toEventually"
),
to: "to eventually",
description: description,
captureExceptions: false
)
verify(pass, msg)
} else {
verify(false, toEventuallyRequiresClosureError)
}
}
/// Tests the actual value using a matcher to not match by checking
/// continuously at each pollInterval until the timeout is reached.
///
/// @discussion
/// This function manages the main run loop (`NSRunLoop.mainRunLoop()`) while this function
/// is executing. Any attempts to touch the run loop may cause non-deterministic behavior.
public func toEventuallyNot<U>(_ matcher: U, timeout: TimeInterval = AsyncDefaults.Timeout, pollInterval: TimeInterval = AsyncDefaults.PollInterval, description: String? = nil)
where U: Matcher, U.ValueType == T {
if expression.isClosure {
let (pass, msg) = expressionDoesNotMatch(
expression,
matcher: async(
style: .toNotMatch,
predicate: matcher.predicate,
timeout: timeout,
poll: pollInterval,
fnName: "toEventuallyNot"
),
toNot: "to eventually not",
description: description
)
verify(pass, msg)
} else {
verify(false, toEventuallyRequiresClosureError)
}
}
/// Tests the actual value using a matcher to not match by checking
/// continuously at each pollInterval until the timeout is reached.
///
/// Alias of toEventuallyNot()
///
/// @discussion
/// This function manages the main run loop (`NSRunLoop.mainRunLoop()`) while this function
/// is executing. Any attempts to touch the run loop may cause non-deterministic behavior.
public func toNotEventually<U>(_ matcher: U, timeout: TimeInterval = AsyncDefaults.Timeout, pollInterval: TimeInterval = AsyncDefaults.PollInterval, description: String? = nil)
where U: Matcher, U.ValueType == T {
return toEventuallyNot(matcher, timeout: timeout, pollInterval: pollInterval, description: description)
}
}
@@ -1,68 +0,0 @@
import Foundation
private func matcherMessage<T>(forType expectedType: T.Type) -> String {
return "be a kind of \(String(describing: expectedType))"
}
private func matcherMessage(forClass expectedClass: AnyClass) -> String {
return "be a kind of \(String(describing: expectedClass))"
}
/// A Nimble matcher that succeeds when the actual value is an instance of the given class.
public func beAKindOf<T>(_ expectedType: T.Type) -> Predicate<Any> {
return Predicate.define { actualExpression in
let message: ExpectationMessage
let instance = try actualExpression.evaluate()
guard let validInstance = instance else {
message = .expectedCustomValueTo(matcherMessage(forType: expectedType), "<nil>")
return PredicateResult(status: .fail, message: message)
}
message = .expectedCustomValueTo(
"be a kind of \(String(describing: expectedType))",
"<\(String(describing: type(of: validInstance))) instance>"
)
return PredicateResult(
bool: validInstance is T,
message: message
)
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
/// A Nimble matcher that succeeds when the actual value is an instance of the given class.
/// @see beAnInstanceOf if you want to match against the exact class
public func beAKindOf(_ expectedClass: AnyClass) -> Predicate<NSObject> {
return Predicate.define { actualExpression in
let message: ExpectationMessage
let status: PredicateStatus
let instance = try actualExpression.evaluate()
if let validInstance = instance {
status = PredicateStatus(bool: instance != nil && instance!.isKind(of: expectedClass))
message = .expectedCustomValueTo(
matcherMessage(forClass: expectedClass),
"<\(String(describing: type(of: validInstance))) instance>"
)
} else {
status = .fail
message = .expectedCustomValueTo(
matcherMessage(forClass: expectedClass),
"<nil>"
)
}
return PredicateResult(status: status, message: message)
}
}
extension NMBObjCMatcher {
@objc public class func beAKindOfMatcher(_ expected: AnyClass) -> NMBMatcher {
return NMBPredicate { actualExpression in
return try beAKindOf(expected).satisfies(actualExpression).toObjectiveC()
}
}
}
#endif
@@ -1,56 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value is an _exact_ instance of the given class.
public func beAnInstanceOf<T>(_ expectedType: T.Type) -> Predicate<Any> {
let errorMessage = "be an instance of \(String(describing: expectedType))"
return Predicate.define { actualExpression in
let instance = try actualExpression.evaluate()
guard let validInstance = instance else {
return PredicateResult(
status: .doesNotMatch,
message: .expectedActualValueTo(errorMessage)
)
}
let actualString = "<\(String(describing: type(of: validInstance))) instance>"
return PredicateResult(
status: PredicateStatus(bool: type(of: validInstance) == expectedType),
message: .expectedCustomValueTo(errorMessage, actualString)
)
}
}
/// A Nimble matcher that succeeds when the actual value is an instance of the given class.
/// @see beAKindOf if you want to match against subclasses
public func beAnInstanceOf(_ expectedClass: AnyClass) -> Predicate<NSObject> {
let errorMessage = "be an instance of \(String(describing: expectedClass))"
return Predicate.define { actualExpression in
let instance = try actualExpression.evaluate()
let actualString: String
if let validInstance = instance {
actualString = "<\(String(describing: type(of: validInstance))) instance>"
} else {
actualString = "<nil>"
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let matches = instance != nil && instance!.isMember(of: expectedClass)
#else
let matches = instance != nil && type(of: instance!) == expectedClass
#endif
return PredicateResult(
status: PredicateStatus(bool: matches),
message: .expectedCustomValueTo(errorMessage, actualString)
)
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beAnInstanceOfMatcher(_ expected: AnyClass) -> NMBMatcher {
return NMBPredicate { actualExpression in
return try beAnInstanceOf(expected).satisfies(actualExpression).toObjectiveC()
}
}
}
#endif
@@ -1,138 +0,0 @@
import Foundation
public let DefaultDelta = 0.0001
internal func isCloseTo(_ actualValue: NMBDoubleConvertible?,
expectedValue: NMBDoubleConvertible,
delta: Double)
-> PredicateResult {
let errorMessage = "be close to <\(stringify(expectedValue))> (within \(stringify(delta)))"
return PredicateResult(
bool: actualValue != nil &&
abs(actualValue!.doubleValue - expectedValue.doubleValue) < delta,
message: .expectedCustomValueTo(errorMessage, "<\(stringify(actualValue))>")
)
}
/// A Nimble matcher that succeeds when a value is close to another. This is used for floating
/// point values which can have imprecise results when doing arithmetic on them.
///
/// @see equal
public func beCloseTo(_ expectedValue: Double, within delta: Double = DefaultDelta) -> Predicate<Double> {
return Predicate.define { actualExpression in
return isCloseTo(try actualExpression.evaluate(), expectedValue: expectedValue, delta: delta)
}
}
/// A Nimble matcher that succeeds when a value is close to another. This is used for floating
/// point values which can have imprecise results when doing arithmetic on them.
///
/// @see equal
public func beCloseTo(_ expectedValue: NMBDoubleConvertible, within delta: Double = DefaultDelta) -> Predicate<NMBDoubleConvertible> {
return Predicate.define { actualExpression in
return isCloseTo(try actualExpression.evaluate(), expectedValue: expectedValue, delta: delta)
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
public class NMBObjCBeCloseToMatcher: NSObject, NMBMatcher {
var _expected: NSNumber
var _delta: CDouble
init(expected: NSNumber, within: CDouble) {
_expected = expected
_delta = within
}
@objc public func matches(_ actualExpression: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
let actualBlock: () -> NMBDoubleConvertible? = ({
return actualExpression() as? NMBDoubleConvertible
})
let expr = Expression(expression: actualBlock, location: location)
let matcher = beCloseTo(self._expected, within: self._delta)
do {
return try matcher.matches(expr, failureMessage: failureMessage)
} catch let error {
failureMessage.stringValue = "unexpected error thrown: <\(error)>"
return false
}
}
@objc public func doesNotMatch(_ actualExpression: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
let actualBlock: () -> NMBDoubleConvertible? = ({
return actualExpression() as? NMBDoubleConvertible
})
let expr = Expression(expression: actualBlock, location: location)
let matcher = beCloseTo(self._expected, within: self._delta)
do {
return try matcher.doesNotMatch(expr, failureMessage: failureMessage)
} catch let error {
failureMessage.stringValue = "unexpected error thrown: <\(error)>"
return false
}
}
@objc public var within: (CDouble) -> NMBObjCBeCloseToMatcher {
return ({ delta in
return NMBObjCBeCloseToMatcher(expected: self._expected, within: delta)
})
}
}
extension NMBObjCMatcher {
@objc public class func beCloseToMatcher(_ expected: NSNumber, within: CDouble) -> NMBObjCBeCloseToMatcher {
return NMBObjCBeCloseToMatcher(expected: expected, within: within)
}
}
#endif
public func beCloseTo(_ expectedValues: [Double], within delta: Double = DefaultDelta) -> Predicate<[Double]> {
let errorMessage = "be close to <\(stringify(expectedValues))> (each within \(stringify(delta)))"
return Predicate.simple(errorMessage) { actualExpression in
if let actual = try actualExpression.evaluate() {
if actual.count != expectedValues.count {
return .doesNotMatch
} else {
for (index, actualItem) in actual.enumerated() {
if fabs(actualItem - expectedValues[index]) > delta {
return .doesNotMatch
}
}
return .matches
}
}
return .doesNotMatch
}
}
// MARK: - Operators
infix operator : ComparisonPrecedence
public func (lhs: Expectation<[Double]>, rhs: [Double]) {
lhs.to(beCloseTo(rhs))
}
public func (lhs: Expectation<NMBDoubleConvertible>, rhs: NMBDoubleConvertible) {
lhs.to(beCloseTo(rhs))
}
public func (lhs: Expectation<NMBDoubleConvertible>, rhs: (expected: NMBDoubleConvertible, delta: Double)) {
lhs.to(beCloseTo(rhs.expected, within: rhs.delta))
}
public func == (lhs: Expectation<NMBDoubleConvertible>, rhs: (expected: NMBDoubleConvertible, delta: Double)) {
lhs.to(beCloseTo(rhs.expected, within: rhs.delta))
}
// make this higher precedence than exponents so the Doubles either end aren't pulled in
// unexpectantly
precedencegroup PlusMinusOperatorPrecedence {
higherThan: BitwiseShiftPrecedence
}
infix operator ± : PlusMinusOperatorPrecedence
public func ±(lhs: NMBDoubleConvertible, rhs: Double) -> (expected: NMBDoubleConvertible, delta: Double) {
return (expected: lhs, delta: rhs)
}
@@ -1,95 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when a value is "empty". For collections, this
/// means the are no items in that collection. For strings, it is an empty string.
public func beEmpty<S: Sequence>() -> Predicate<S> {
return Predicate.simple("be empty") { actualExpression in
let actualSeq = try actualExpression.evaluate()
if actualSeq == nil {
return .fail
}
var generator = actualSeq!.makeIterator()
return PredicateStatus(bool: generator.next() == nil)
}
}
/// A Nimble matcher that succeeds when a value is "empty". For collections, this
/// means the are no items in that collection. For strings, it is an empty string.
public func beEmpty() -> Predicate<String> {
return Predicate.simple("be empty") { actualExpression in
let actualString = try actualExpression.evaluate()
return PredicateStatus(bool: actualString == nil || NSString(string: actualString!).length == 0)
}
}
/// A Nimble matcher that succeeds when a value is "empty". For collections, this
/// means the are no items in that collection. For NSString instances, it is an empty string.
public func beEmpty() -> Predicate<NSString> {
return Predicate.simple("be empty") { actualExpression in
let actualString = try actualExpression.evaluate()
return PredicateStatus(bool: actualString == nil || actualString!.length == 0)
}
}
// Without specific overrides, beEmpty() is ambiguous for NSDictionary, NSArray,
// etc, since they conform to Sequence as well as NMBCollection.
/// A Nimble matcher that succeeds when a value is "empty". For collections, this
/// means the are no items in that collection. For strings, it is an empty string.
public func beEmpty() -> Predicate<NSDictionary> {
return Predicate.simple("be empty") { actualExpression in
let actualDictionary = try actualExpression.evaluate()
return PredicateStatus(bool: actualDictionary == nil || actualDictionary!.count == 0)
}
}
/// A Nimble matcher that succeeds when a value is "empty". For collections, this
/// means the are no items in that collection. For strings, it is an empty string.
public func beEmpty() -> Predicate<NSArray> {
return Predicate.simple("be empty") { actualExpression in
let actualArray = try actualExpression.evaluate()
return PredicateStatus(bool: actualArray == nil || actualArray!.count == 0)
}
}
/// A Nimble matcher that succeeds when a value is "empty". For collections, this
/// means the are no items in that collection. For strings, it is an empty string.
public func beEmpty() -> Predicate<NMBCollection> {
return Predicate.simple("be empty") { actualExpression in
let actual = try actualExpression.evaluate()
return PredicateStatus(bool: actual == nil || actual!.count == 0)
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beEmptyMatcher() -> NMBPredicate {
return NMBPredicate { actualExpression in
let location = actualExpression.location
let actualValue = try actualExpression.evaluate()
if let value = actualValue as? NMBCollection {
let expr = Expression(expression: ({ value as NMBCollection }), location: location)
return try beEmpty().satisfies(expr).toObjectiveC()
} else if let value = actualValue as? NSString {
let expr = Expression(expression: ({ value as String }), location: location)
return try beEmpty().satisfies(expr).toObjectiveC()
} else if let actualValue = actualValue {
// swiftlint:disable:next line_length
let badTypeErrorMsg = "be empty (only works for NSArrays, NSSets, NSIndexSets, NSDictionaries, NSHashTables, and NSStrings)"
return NMBPredicateResult(
status: NMBPredicateStatus.fail,
message: NMBExpectationMessage(
expectedActualValueTo: badTypeErrorMsg,
customActualValue: "\(String(describing: type(of: actualValue))) type"
)
)
}
return NMBPredicateResult(
status: NMBPredicateStatus.fail,
message: NMBExpectationMessage(expectedActualValueTo: "be empty").appendedBeNilHint()
)
}
}
}
#endif
@@ -1,42 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value is greater than the expected value.
public func beGreaterThan<T: Comparable>(_ expectedValue: T?) -> Predicate<T> {
let errorMessage = "be greater than <\(stringify(expectedValue))>"
return Predicate.simple(errorMessage) { actualExpression in
if let actual = try actualExpression.evaluate(), let expected = expectedValue {
return PredicateStatus(bool: actual > expected)
}
return .fail
}
}
/// A Nimble matcher that succeeds when the actual value is greater than the expected value.
public func beGreaterThan(_ expectedValue: NMBComparable?) -> Predicate<NMBComparable> {
let errorMessage = "be greater than <\(stringify(expectedValue))>"
return Predicate.simple(errorMessage) { actualExpression in
let actualValue = try actualExpression.evaluate()
let matches = actualValue != nil
&& actualValue!.NMB_compare(expectedValue) == ComparisonResult.orderedDescending
return PredicateStatus(bool: matches)
}
}
public func ><T: Comparable>(lhs: Expectation<T>, rhs: T) {
lhs.to(beGreaterThan(rhs))
}
public func > (lhs: Expectation<NMBComparable>, rhs: NMBComparable?) {
lhs.to(beGreaterThan(rhs))
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beGreaterThanMatcher(_ expected: NMBComparable?) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let expr = actualExpression.cast { $0 as? NMBComparable }
return try beGreaterThan(expected).matches(expr, failureMessage: failureMessage)
}
}
}
#endif
@@ -1,44 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value is greater than
/// or equal to the expected value.
public func beGreaterThanOrEqualTo<T: Comparable>(_ expectedValue: T?) -> Predicate<T> {
let message = "be greater than or equal to <\(stringify(expectedValue))>"
return Predicate.simple(message) { actualExpression in
let actualValue = try actualExpression.evaluate()
if let actual = actualValue, let expected = expectedValue {
return PredicateStatus(bool: actual >= expected)
}
return .fail
}
}
/// A Nimble matcher that succeeds when the actual value is greater than
/// or equal to the expected value.
public func beGreaterThanOrEqualTo<T: NMBComparable>(_ expectedValue: T?) -> Predicate<T> {
let message = "be greater than or equal to <\(stringify(expectedValue))>"
return Predicate.simple(message) { actualExpression in
let actualValue = try actualExpression.evaluate()
let matches = actualValue != nil && actualValue!.NMB_compare(expectedValue) != ComparisonResult.orderedAscending
return PredicateStatus(bool: matches)
}
}
public func >=<T: Comparable>(lhs: Expectation<T>, rhs: T) {
lhs.to(beGreaterThanOrEqualTo(rhs))
}
public func >=<T: NMBComparable>(lhs: Expectation<T>, rhs: T) {
lhs.to(beGreaterThanOrEqualTo(rhs))
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beGreaterThanOrEqualToMatcher(_ expected: NMBComparable?) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let expr = actualExpression.cast { $0 as? NMBComparable }
return try beGreaterThanOrEqualTo(expected).matches(expr, failureMessage: failureMessage)
}
}
}
#endif
@@ -1,53 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value is the same instance
/// as the expected instance.
public func beIdenticalTo(_ expected: Any?) -> Predicate<Any> {
return Predicate.define { actualExpression in
#if os(Linux)
let actual = try actualExpression.evaluate() as? AnyObject
#else
let actual = try actualExpression.evaluate() as AnyObject?
#endif
let bool: Bool
#if os(Linux)
bool = actual === (expected as? AnyObject) && actual !== nil
#else
bool = actual === (expected as AnyObject?) && actual !== nil
#endif
return PredicateResult(
bool: bool,
message: .expectedCustomValueTo(
"be identical to \(identityAsString(expected))",
"\(identityAsString(actual))"
)
)
}
}
public func === (lhs: Expectation<Any>, rhs: Any?) {
lhs.to(beIdenticalTo(rhs))
}
public func !== (lhs: Expectation<Any>, rhs: Any?) {
lhs.toNot(beIdenticalTo(rhs))
}
/// A Nimble matcher that succeeds when the actual value is the same instance
/// as the expected instance.
///
/// Alias for "beIdenticalTo".
public func be(_ expected: Any?) -> Predicate<Any> {
return beIdenticalTo(expected)
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beIdenticalToMatcher(_ expected: NSObject?) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let aExpr = actualExpression.cast { $0 as Any? }
return try beIdenticalTo(expected).matches(aExpr, failureMessage: failureMessage)
}
}
}
#endif
@@ -1,41 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value is less than the expected value.
public func beLessThan<T: Comparable>(_ expectedValue: T?) -> Predicate<T> {
let message = "be less than <\(stringify(expectedValue))>"
return Predicate.simple(message) { actualExpression in
if let actual = try actualExpression.evaluate(), let expected = expectedValue {
return PredicateStatus(bool: actual < expected)
}
return .fail
}
}
/// A Nimble matcher that succeeds when the actual value is less than the expected value.
public func beLessThan(_ expectedValue: NMBComparable?) -> Predicate<NMBComparable> {
let message = "be less than <\(stringify(expectedValue))>"
return Predicate.simple(message) { actualExpression in
let actualValue = try actualExpression.evaluate()
let matches = actualValue != nil && actualValue!.NMB_compare(expectedValue) == ComparisonResult.orderedAscending
return PredicateStatus(bool: matches)
}
}
public func <<T: Comparable>(lhs: Expectation<T>, rhs: T) {
lhs.to(beLessThan(rhs))
}
public func < (lhs: Expectation<NMBComparable>, rhs: NMBComparable?) {
lhs.to(beLessThan(rhs))
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beLessThanMatcher(_ expected: NMBComparable?) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let expr = actualExpression.cast { $0 as? NMBComparable }
return try beLessThan(expected).matches(expr, failureMessage: failureMessage)
}
}
}
#endif
@@ -1,41 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value is less than
/// or equal to the expected value.
public func beLessThanOrEqualTo<T: Comparable>(_ expectedValue: T?) -> Predicate<T> {
return Predicate.simple("be less than or equal to <\(stringify(expectedValue))>") { actualExpression in
if let actual = try actualExpression.evaluate(), let expected = expectedValue {
return PredicateStatus(bool: actual <= expected)
}
return .fail
}
}
/// A Nimble matcher that succeeds when the actual value is less than
/// or equal to the expected value.
public func beLessThanOrEqualTo<T: NMBComparable>(_ expectedValue: T?) -> Predicate<T> {
return Predicate.simple("be less than or equal to <\(stringify(expectedValue))>") { actualExpression in
let actualValue = try actualExpression.evaluate()
let matches = actualValue.map { $0.NMB_compare(expectedValue) != .orderedDescending } ?? false
return PredicateStatus(bool: matches)
}
}
public func <=<T: Comparable>(lhs: Expectation<T>, rhs: T) {
lhs.to(beLessThanOrEqualTo(rhs))
}
public func <=<T: NMBComparable>(lhs: Expectation<T>, rhs: T) {
lhs.to(beLessThanOrEqualTo(rhs))
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beLessThanOrEqualToMatcher(_ expected: NMBComparable?) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let expr = actualExpression.cast { $0 as? NMBComparable }
return try beLessThanOrEqualTo(expected).matches(expr, failureMessage: failureMessage)
}
}
}
#endif
@@ -1,167 +0,0 @@
import Foundation
extension Int8: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).int8Value
}
}
extension UInt8: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).uint8Value
}
}
extension Int16: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).int16Value
}
}
extension UInt16: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).uint16Value
}
}
extension Int32: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).int32Value
}
}
extension UInt32: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).uint32Value
}
}
extension Int64: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).int64Value
}
}
extension UInt64: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).uint64Value
}
}
extension Float: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).floatValue
}
}
extension Double: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).doubleValue
}
}
extension Int: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).intValue
}
}
extension UInt: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = NSNumber(value: value).uintValue
}
}
internal func rename<T>(_ matcher: Predicate<T>, failureMessage message: ExpectationMessage) -> Predicate<T> {
return Predicate { actualExpression in
let result = try matcher.satisfies(actualExpression)
return PredicateResult(status: result.status, message: message)
}.requireNonNil
}
// MARK: beTrue() / beFalse()
/// A Nimble matcher that succeeds when the actual value is exactly true.
/// This matcher will not match against nils.
public func beTrue() -> Predicate<Bool> {
return rename(equal(true), failureMessage: .expectedActualValueTo("be true"))
}
/// A Nimble matcher that succeeds when the actual value is exactly false.
/// This matcher will not match against nils.
public func beFalse() -> Predicate<Bool> {
return rename(equal(false), failureMessage: .expectedActualValueTo("be false"))
}
// MARK: beTruthy() / beFalsy()
/// A Nimble matcher that succeeds when the actual value is not logically false.
public func beTruthy<T: ExpressibleByBooleanLiteral & Equatable>() -> Predicate<T> {
return Predicate.simpleNilable("be truthy") { actualExpression in
let actualValue = try actualExpression.evaluate()
if let actualValue = actualValue {
// FIXME: This is a workaround to SR-2290.
// See:
// - https://bugs.swift.org/browse/SR-2290
// - https://github.com/norio-nomura/Nimble/pull/5#issuecomment-237835873
if let number = actualValue as? NSNumber {
return PredicateStatus(bool: number.boolValue == true)
}
return PredicateStatus(bool: actualValue == (true as T))
}
return PredicateStatus(bool: actualValue != nil)
}
}
/// A Nimble matcher that succeeds when the actual value is logically false.
/// This matcher will match against nils.
public func beFalsy<T: ExpressibleByBooleanLiteral & Equatable>() -> Predicate<T> {
return Predicate.simpleNilable("be falsy") { actualExpression in
let actualValue = try actualExpression.evaluate()
if let actualValue = actualValue {
// FIXME: This is a workaround to SR-2290.
// See:
// - https://bugs.swift.org/browse/SR-2290
// - https://github.com/norio-nomura/Nimble/pull/5#issuecomment-237835873
if let number = actualValue as? NSNumber {
return PredicateStatus(bool: number.boolValue == false)
}
return PredicateStatus(bool: actualValue == (false as T))
}
return PredicateStatus(bool: actualValue == nil)
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beTruthyMatcher() -> NMBObjCMatcher {
return NMBObjCMatcher { actualExpression, failureMessage in
let expr = actualExpression.cast { ($0 as? NSNumber)?.boolValue ?? false }
return try beTruthy().matches(expr, failureMessage: failureMessage)
}
}
@objc public class func beFalsyMatcher() -> NMBObjCMatcher {
return NMBObjCMatcher { actualExpression, failureMessage in
let expr = actualExpression.cast { ($0 as? NSNumber)?.boolValue ?? false }
return try beFalsy().matches(expr, failureMessage: failureMessage)
}
}
@objc public class func beTrueMatcher() -> NMBObjCMatcher {
return NMBObjCMatcher { actualExpression, failureMessage in
let expr = actualExpression.cast { ($0 as? NSNumber)?.boolValue ?? false }
return try beTrue().matches(expr, failureMessage: failureMessage)
}
}
@objc public class func beFalseMatcher() -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let expr = actualExpression.cast { ($0 as? NSNumber)?.boolValue ?? false }
return try beFalse().matches(expr, failureMessage: failureMessage)
}
}
}
#endif
-19
View File
@@ -1,19 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value is nil.
public func beNil<T>() -> Predicate<T> {
return Predicate.simpleNilable("be nil") { actualExpression in
let actualValue = try actualExpression.evaluate()
return PredicateStatus(bool: actualValue == nil)
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beNilMatcher() -> NMBObjCMatcher {
return NMBObjCMatcher { actualExpression, failureMessage in
return try beNil().matches(actualExpression, failureMessage: failureMessage)
}
}
}
#endif
-17
View File
@@ -1,17 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value is Void.
public func beVoid() -> Predicate<()> {
return Predicate.simpleNilable("be void") { actualExpression in
let actualValue: ()? = try actualExpression.evaluate()
return PredicateStatus(bool: actualValue != nil)
}
}
public func == (lhs: Expectation<()>, rhs: ()) {
lhs.to(beVoid())
}
public func != (lhs: Expectation<()>, rhs: ()) {
lhs.toNot(beVoid())
}
@@ -1,60 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual sequence's first element
/// is equal to the expected value.
public func beginWith<S: Sequence, T: Equatable>(_ startingElement: T) -> Predicate<S>
where S.Iterator.Element == T {
return Predicate.simple("begin with <\(startingElement)>") { actualExpression in
if let actualValue = try actualExpression.evaluate() {
var actualGenerator = actualValue.makeIterator()
return PredicateStatus(bool: actualGenerator.next() == startingElement)
}
return .fail
}
}
/// A Nimble matcher that succeeds when the actual collection's first element
/// is equal to the expected object.
public func beginWith(_ startingElement: Any) -> Predicate<NMBOrderedCollection> {
return Predicate.simple("begin with <\(startingElement)>") { actualExpression in
guard let collection = try actualExpression.evaluate() else { return .fail }
guard collection.count > 0 else { return .doesNotMatch }
#if os(Linux)
guard let collectionValue = collection.object(at: 0) as? NSObject else {
return .fail
}
#else
let collectionValue = collection.object(at: 0) as AnyObject
#endif
return PredicateStatus(bool: collectionValue.isEqual(startingElement))
}
}
/// A Nimble matcher that succeeds when the actual string contains expected substring
/// where the expected substring's location is zero.
public func beginWith(_ startingSubstring: String) -> Predicate<String> {
return Predicate.simple("begin with <\(startingSubstring)>") { actualExpression in
if let actual = try actualExpression.evaluate() {
let range = actual.range(of: startingSubstring)
return PredicateStatus(bool: range != nil && range!.lowerBound == actual.startIndex)
}
return .fail
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beginWithMatcher(_ expected: Any) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let actual = try actualExpression.evaluate()
if (actual as? String) != nil {
let expr = actualExpression.cast { $0 as? String }
return try beginWith(expected as! String).matches(expr, failureMessage: failureMessage)
} else {
let expr = actualExpression.cast { $0 as? NMBOrderedCollection }
return try beginWith(expected).matches(expr, failureMessage: failureMessage)
}
}
}
}
#endif
@@ -1,95 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual sequence contains the expected value.
public func contain<S: Sequence, T: Equatable>(_ items: T...) -> Predicate<S>
where S.Iterator.Element == T {
return contain(items)
}
public func contain<S: Sequence, T: Equatable>(_ items: [T]) -> Predicate<S>
where S.Iterator.Element == T {
return Predicate.simple("contain <\(arrayAsString(items))>") { actualExpression in
if let actual = try actualExpression.evaluate() {
let matches = items.all {
return actual.contains($0)
}
return PredicateStatus(bool: matches)
}
return .fail
}
}
/// A Nimble matcher that succeeds when the actual string contains the expected substring.
public func contain(_ substrings: String...) -> Predicate<String> {
return contain(substrings)
}
public func contain(_ substrings: [String]) -> Predicate<String> {
return Predicate.simple("contain <\(arrayAsString(substrings))>") { actualExpression in
if let actual = try actualExpression.evaluate() {
let matches = substrings.all {
let range = actual.range(of: $0)
return range != nil && !range!.isEmpty
}
return PredicateStatus(bool: matches)
}
return .fail
}
}
/// A Nimble matcher that succeeds when the actual string contains the expected substring.
public func contain(_ substrings: NSString...) -> Predicate<NSString> {
return contain(substrings)
}
public func contain(_ substrings: [NSString]) -> Predicate<NSString> {
return Predicate.simple("contain <\(arrayAsString(substrings))>") { actualExpression in
if let actual = try actualExpression.evaluate() {
let matches = substrings.all { actual.range(of: $0.description).length != 0 }
return PredicateStatus(bool: matches)
}
return .fail
}
}
/// A Nimble matcher that succeeds when the actual collection contains the expected object.
public func contain(_ items: Any?...) -> Predicate<NMBContainer> {
return contain(items)
}
public func contain(_ items: [Any?]) -> Predicate<NMBContainer> {
return Predicate.simple("contain <\(arrayAsString(items))>") { actualExpression in
guard let actual = try actualExpression.evaluate() else { return .fail }
let matches = items.all { item in
return item.map { actual.contains($0) } ?? false
}
return PredicateStatus(bool: matches)
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func containMatcher(_ expected: [NSObject]) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let location = actualExpression.location
let actualValue = try actualExpression.evaluate()
if let value = actualValue as? NMBContainer {
let expr = Expression(expression: ({ value as NMBContainer }), location: location)
// A straightforward cast on the array causes this to crash, so we have to cast the individual items
let expectedOptionals: [Any?] = expected.map({ $0 as Any? })
return try contain(expectedOptionals).matches(expr, failureMessage: failureMessage)
} else if let value = actualValue as? NSString {
let expr = Expression(expression: ({ value as String }), location: location)
return try contain(expected as! [String]).matches(expr, failureMessage: failureMessage)
} else if actualValue != nil {
// swiftlint:disable:next line_length
failureMessage.postfixMessage = "contain <\(arrayAsString(expected))> (only works for NSArrays, NSSets, NSHashTables, and NSStrings)"
} else {
failureMessage.postfixMessage = "contain <\(arrayAsString(expected))>"
}
return false
}
}
}
#endif
@@ -1,58 +0,0 @@
import Foundation
public func containElementSatisfying<S: Sequence, T>(_ predicate: @escaping ((T) -> Bool), _ predicateDescription: String = "") -> Predicate<S> where S.Iterator.Element == T {
return Predicate.define { actualExpression in
let message: ExpectationMessage
if predicateDescription == "" {
message = .expectedTo("find object in collection that satisfies predicate")
} else {
message = .expectedTo("find object in collection \(predicateDescription)")
}
if let sequence = try actualExpression.evaluate() {
for object in sequence {
if predicate(object) {
return PredicateResult(bool: true, message: message)
}
}
return PredicateResult(bool: false, message: message)
}
return PredicateResult(status: .fail, message: message)
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func containElementSatisfyingMatcher(_ predicate: @escaping ((NSObject) -> Bool)) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let value = try actualExpression.evaluate()
guard let enumeration = value as? NSFastEnumeration else {
// swiftlint:disable:next line_length
failureMessage.postfixMessage = "containElementSatisfying must be provided an NSFastEnumeration object"
failureMessage.actualValue = nil
failureMessage.expected = ""
failureMessage.to = ""
return false
}
var iterator = NSFastEnumerationIterator(enumeration)
while let item = iterator.next() {
guard let object = item as? NSObject else {
continue
}
if predicate(object) {
return true
}
}
failureMessage.actualValue = nil
failureMessage.postfixMessage = "find object in collection that satisfies predicate"
return false
}
}
}
#endif
@@ -1,68 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual sequence's last element
/// is equal to the expected value.
public func endWith<S: Sequence, T: Equatable>(_ endingElement: T) -> Predicate<S>
where S.Iterator.Element == T {
return Predicate.simple("end with <\(endingElement)>") { actualExpression in
if let actualValue = try actualExpression.evaluate() {
var actualGenerator = actualValue.makeIterator()
var lastItem: T?
var item: T?
repeat {
lastItem = item
item = actualGenerator.next()
} while(item != nil)
return PredicateStatus(bool: lastItem == endingElement)
}
return .fail
}
}
/// A Nimble matcher that succeeds when the actual collection's last element
/// is equal to the expected object.
public func endWith(_ endingElement: Any) -> Predicate<NMBOrderedCollection> {
return Predicate.simple("end with <\(endingElement)>") { actualExpression in
guard let collection = try actualExpression.evaluate() else { return .fail }
guard collection.count > 0 else { return PredicateStatus(bool: false) }
#if os(Linux)
guard let collectionValue = collection.object(at: collection.count - 1) as? NSObject else {
return .fail
}
#else
let collectionValue = collection.object(at: collection.count - 1) as AnyObject
#endif
return PredicateStatus(bool: collectionValue.isEqual(endingElement))
}
}
/// A Nimble matcher that succeeds when the actual string contains the expected substring
/// where the expected substring's location is the actual string's length minus the
/// expected substring's length.
public func endWith(_ endingSubstring: String) -> Predicate<String> {
return Predicate.simple("end with <\(endingSubstring)>") { actualExpression in
if let collection = try actualExpression.evaluate() {
return PredicateStatus(bool: collection.hasSuffix(endingSubstring))
}
return .fail
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func endWithMatcher(_ expected: Any) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let actual = try actualExpression.evaluate()
if (actual as? String) != nil {
let expr = actualExpression.cast { $0 as? String }
return try endWith(expected as! String).matches(expr, failureMessage: failureMessage)
} else {
let expr = actualExpression.cast { $0 as? NMBOrderedCollection }
return try endWith(expected).matches(expr, failureMessage: failureMessage)
}
}
}
}
#endif
-220
View File
@@ -1,220 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value is equal to the expected value.
/// Values can support equal by supporting the Equatable protocol.
///
/// @see beCloseTo if you want to match imprecise types (eg - floats, doubles).
public func equal<T: Equatable>(_ expectedValue: T?) -> Predicate<T> {
return Predicate.define("equal <\(stringify(expectedValue))>") { actualExpression, msg in
let actualValue = try actualExpression.evaluate()
let matches = actualValue == expectedValue && expectedValue != nil
if expectedValue == nil || actualValue == nil {
if expectedValue == nil && actualValue != nil {
return PredicateResult(
status: .fail,
message: msg.appendedBeNilHint()
)
}
return PredicateResult(status: .fail, message: msg)
}
return PredicateResult(status: PredicateStatus(bool: matches), message: msg)
}
}
/// A Nimble matcher that succeeds when the actual value is equal to the expected value.
/// Values can support equal by supporting the Equatable protocol.
///
/// @see beCloseTo if you want to match imprecise types (eg - floats, doubles).
public func equal<T, C: Equatable>(_ expectedValue: [T: C]?) -> Predicate<[T: C]> {
return Predicate.define("equal <\(stringify(expectedValue))>") { actualExpression, msg in
let actualValue = try actualExpression.evaluate()
if expectedValue == nil || actualValue == nil {
if expectedValue == nil && actualValue != nil {
return PredicateResult(
status: .fail,
message: msg.appendedBeNilHint()
)
}
return PredicateResult(status: .fail, message: msg)
}
return PredicateResult(
status: PredicateStatus(bool: expectedValue! == actualValue!),
message: msg
)
}
}
/// A Nimble matcher that succeeds when the actual collection is equal to the expected collection.
/// Items must implement the Equatable protocol.
public func equal<T: Equatable>(_ expectedValue: [T]?) -> Predicate<[T]> {
return Predicate.define("equal <\(stringify(expectedValue))>") { actualExpression, msg in
let actualValue = try actualExpression.evaluate()
if expectedValue == nil || actualValue == nil {
if expectedValue == nil && actualValue != nil {
return PredicateResult(
status: .fail,
message: msg.appendedBeNilHint()
)
}
return PredicateResult(
status: .fail,
message: msg
)
}
return PredicateResult(
bool: expectedValue! == actualValue!,
message: msg
)
}
}
/// A Nimble matcher allowing comparison of collection with optional type
public func equal<T: Equatable>(_ expectedValue: [T?]) -> Predicate<[T?]> {
return Predicate.define("equal <\(stringify(expectedValue))>") { actualExpression, msg in
if let actualValue = try actualExpression.evaluate() {
let doesNotMatch = PredicateResult(
status: .doesNotMatch,
message: msg
)
if expectedValue.count != actualValue.count {
return doesNotMatch
}
for (index, item) in actualValue.enumerated() {
let otherItem = expectedValue[index]
if item == nil && otherItem == nil {
continue
} else if item == nil && otherItem != nil {
return doesNotMatch
} else if item != nil && otherItem == nil {
return doesNotMatch
} else if item! != otherItem! {
return doesNotMatch
}
}
return PredicateResult(
status: .matches,
message: msg
)
} else {
return PredicateResult(
status: .fail,
message: msg.appendedBeNilHint()
)
}
}
}
/// A Nimble matcher that succeeds when the actual set is equal to the expected set.
public func equal<T>(_ expectedValue: Set<T>?) -> Predicate<Set<T>> {
return equal(expectedValue, stringify: { stringify($0) })
}
/// A Nimble matcher that succeeds when the actual set is equal to the expected set.
public func equal<T: Comparable>(_ expectedValue: Set<T>?) -> Predicate<Set<T>> {
return equal(expectedValue, stringify: {
if let set = $0 {
return stringify(Array(set).sorted { $0 < $1 })
} else {
return "nil"
}
})
}
private func equal<T>(_ expectedValue: Set<T>?, stringify: @escaping (Set<T>?) -> String) -> Predicate<Set<T>> {
return Predicate { actualExpression in
var errorMessage: ExpectationMessage =
.expectedActualValueTo("equal <\(stringify(expectedValue))>")
if let expectedValue = expectedValue {
if let actualValue = try actualExpression.evaluate() {
errorMessage = .expectedCustomValueTo(
"equal <\(stringify(expectedValue))>",
"<\(stringify(actualValue))>"
)
if expectedValue == actualValue {
return PredicateResult(
status: .matches,
message: errorMessage
)
}
let missing = expectedValue.subtracting(actualValue)
if missing.count > 0 {
errorMessage = errorMessage.appended(message: ", missing <\(stringify(missing))>")
}
let extra = actualValue.subtracting(expectedValue)
if extra.count > 0 {
errorMessage = errorMessage.appended(message: ", extra <\(stringify(extra))>")
}
return PredicateResult(
status: .doesNotMatch,
message: errorMessage
)
}
return PredicateResult(
status: .fail,
message: errorMessage.appendedBeNilHint()
)
} else {
return PredicateResult(
status: .fail,
message: errorMessage.appendedBeNilHint()
)
}
}
}
public func ==<T: Equatable>(lhs: Expectation<T>, rhs: T?) {
lhs.to(equal(rhs))
}
public func !=<T: Equatable>(lhs: Expectation<T>, rhs: T?) {
lhs.toNot(equal(rhs))
}
public func ==<T: Equatable>(lhs: Expectation<[T]>, rhs: [T]?) {
lhs.to(equal(rhs))
}
public func !=<T: Equatable>(lhs: Expectation<[T]>, rhs: [T]?) {
lhs.toNot(equal(rhs))
}
public func == <T>(lhs: Expectation<Set<T>>, rhs: Set<T>?) {
lhs.to(equal(rhs))
}
public func != <T>(lhs: Expectation<Set<T>>, rhs: Set<T>?) {
lhs.toNot(equal(rhs))
}
public func ==<T: Comparable>(lhs: Expectation<Set<T>>, rhs: Set<T>?) {
lhs.to(equal(rhs))
}
public func !=<T: Comparable>(lhs: Expectation<Set<T>>, rhs: Set<T>?) {
lhs.toNot(equal(rhs))
}
public func ==<T, C: Equatable>(lhs: Expectation<[T: C]>, rhs: [T: C]?) {
lhs.to(equal(rhs))
}
public func !=<T, C: Equatable>(lhs: Expectation<[T: C]>, rhs: [T: C]?) {
lhs.toNot(equal(rhs))
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func equalMatcher(_ expected: NSObject) -> NMBMatcher {
return NMBPredicate { actualExpression in
return try equal(expected).satisfies(actualExpression).toObjectiveC()
}
}
}
#endif
@@ -1,65 +0,0 @@
import Foundation
// The `haveCount` matchers do not print the full string representation of the collection value,
// instead they only print the type name and the expected count. This makes it easier to understand
// the reason for failed expectations. See: https://github.com/Quick/Nimble/issues/308.
// The representation of the collection content is provided in a new line as an `extendedMessage`.
/// A Nimble matcher that succeeds when the actual Collection's count equals
/// the expected value
public func haveCount<T: Collection>(_ expectedValue: T.IndexDistance) -> Predicate<T> {
return Predicate.define { actualExpression in
if let actualValue = try actualExpression.evaluate() {
let message = ExpectationMessage
.expectedCustomValueTo(
"have \(prettyCollectionType(actualValue)) with count \(stringify(expectedValue))",
"\(actualValue.count)"
)
.appended(details: "Actual Value: \(stringify(actualValue))")
let result = expectedValue == actualValue.count
return PredicateResult(bool: result, message: message)
} else {
return PredicateResult(status: .fail, message: .fail(""))
}
}
}
/// A Nimble matcher that succeeds when the actual collection's count equals
/// the expected value
public func haveCount(_ expectedValue: Int) -> Predicate<NMBCollection> {
return Predicate { actualExpression in
if let actualValue = try actualExpression.evaluate() {
let message = ExpectationMessage
.expectedCustomValueTo(
"have \(prettyCollectionType(actualValue)) with count \(stringify(expectedValue))",
"\(actualValue.count)"
)
.appended(details: "Actual Value: \(stringify(actualValue))")
let result = expectedValue == actualValue.count
return PredicateResult(bool: result, message: message)
} else {
return PredicateResult(status: .fail, message: .fail(""))
}
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func haveCountMatcher(_ expected: NSNumber) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let location = actualExpression.location
let actualValue = try actualExpression.evaluate()
if let value = actualValue as? NMBCollection {
let expr = Expression(expression: ({ value as NMBCollection}), location: location)
return try haveCount(expected.intValue).matches(expr, failureMessage: failureMessage)
} else if let actualValue = actualValue {
failureMessage.postfixMessage = "get type of NSArray, NSSet, NSDictionary, or NSHashTable"
failureMessage.actualValue = "\(String(describing: type(of: actualValue)))"
}
return false
}
}
}
#endif
-29
View File
@@ -1,29 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual string satisfies the regular expression
/// described by the expected string.
public func match(_ expectedValue: String?) -> Predicate<String> {
return Predicate.simple("match <\(stringify(expectedValue))>") { actualExpression in
if let actual = try actualExpression.evaluate() {
if let regexp = expectedValue {
let bool = actual.range(of: regexp, options: .regularExpression) != nil
return PredicateStatus(bool: bool)
}
}
return .fail
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func matchMatcher(_ expected: NSString) -> NMBMatcher {
return NMBPredicate { actualExpression in
let actual = actualExpression.cast { $0 as? String }
return try match(expected.description).satisfies(actual).toObjectiveC()
}
}
}
#endif
@@ -1,76 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual expression evaluates to an
/// error from the specified case.
///
/// Errors are tried to be compared by their implementation of Equatable,
/// otherwise they fallback to comparison by _domain and _code.
public func matchError<T: Error>(_ error: T) -> Predicate<Error> {
return Predicate.define { actualExpression in
let actualError = try actualExpression.evaluate()
let failureMessage = FailureMessage()
setFailureMessageForError(
failureMessage,
postfixMessageVerb: "match",
actualError: actualError,
error: error
)
var matches = false
if let actualError = actualError, errorMatchesExpectedError(actualError, expectedError: error) {
matches = true
}
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
/// A Nimble matcher that succeeds when the actual expression evaluates to an
/// error from the specified case.
///
/// Errors are tried to be compared by their implementation of Equatable,
/// otherwise they fallback to comparision by _domain and _code.
public func matchError<T: Error & Equatable>(_ error: T) -> Predicate<Error> {
return Predicate.define { actualExpression in
let actualError = try actualExpression.evaluate()
let failureMessage = FailureMessage()
setFailureMessageForError(
failureMessage,
postfixMessageVerb: "match",
actualError: actualError,
error: error
)
var matches = false
if let actualError = actualError as? T, error == actualError {
matches = true
}
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
/// A Nimble matcher that succeeds when the actual expression evaluates to an
/// error of the specified type
public func matchError<T: Error>(_ errorType: T.Type) -> Predicate<Error> {
return Predicate.define { actualExpression in
let actualError = try actualExpression.evaluate()
let failureMessage = FailureMessage()
setFailureMessageForError(
failureMessage,
postfixMessageVerb: "match",
actualError: actualError,
errorType: errorType
)
var matches = false
if actualError as? T != nil {
matches = true
}
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
@@ -1,85 +0,0 @@
/// DEPRECATED: A convenience API to build matchers that don't need special negation
/// behavior. The toNot() behavior is the negation of to().
///
/// @see NonNilMatcherFunc if you prefer to have this matcher fail when nil
/// values are received in an expectation.
///
/// You may use this when implementing your own custom matchers.
///
/// Use the Matcher protocol instead of this type to accept custom matchers as
/// input parameters.
/// @see allPass for an example that uses accepts other matchers as input.
@available(*, deprecated, message: "Use to Predicate instead")
public struct MatcherFunc<T>: Matcher {
public let matcher: (Expression<T>, FailureMessage) throws -> Bool
public init(_ matcher: @escaping (Expression<T>, FailureMessage) throws -> Bool) {
self.matcher = matcher
}
public func matches(_ actualExpression: Expression<T>, failureMessage: FailureMessage) throws -> Bool {
return try matcher(actualExpression, failureMessage)
}
public func doesNotMatch(_ actualExpression: Expression<T>, failureMessage: FailureMessage) throws -> Bool {
return try !matcher(actualExpression, failureMessage)
}
/// Compatibility layer to new Matcher API. Converts an old-style matcher to a new one.
/// Note: You should definitely spend the time to convert to the new api as soon as possible
/// since this struct type is deprecated.
public var predicate: Predicate<T> {
return Predicate.fromDeprecatedMatcher(self)
}
}
/// DEPRECATED: A convenience API to build matchers that don't need special negation
/// behavior. The toNot() behavior is the negation of to().
///
/// Unlike MatcherFunc, this will always fail if an expectation contains nil.
/// This applies regardless of using to() or toNot().
///
/// You may use this when implementing your own custom matchers.
///
/// Use the Matcher protocol instead of this type to accept custom matchers as
/// input parameters.
/// @see allPass for an example that uses accepts other matchers as input.
@available(*, deprecated, message: "Use to Predicate instead")
public struct NonNilMatcherFunc<T>: Matcher {
public let matcher: (Expression<T>, FailureMessage) throws -> Bool
public init(_ matcher: @escaping (Expression<T>, FailureMessage) throws -> Bool) {
self.matcher = matcher
}
public func matches(_ actualExpression: Expression<T>, failureMessage: FailureMessage) throws -> Bool {
let pass = try matcher(actualExpression, failureMessage)
if try attachNilErrorIfNeeded(actualExpression, failureMessage: failureMessage) {
return false
}
return pass
}
public func doesNotMatch(_ actualExpression: Expression<T>, failureMessage: FailureMessage) throws -> Bool {
let pass = try !matcher(actualExpression, failureMessage)
if try attachNilErrorIfNeeded(actualExpression, failureMessage: failureMessage) {
return false
}
return pass
}
internal func attachNilErrorIfNeeded(_ actualExpression: Expression<T>, failureMessage: FailureMessage) throws -> Bool {
if try actualExpression.evaluate() == nil {
failureMessage.postfixActual = " (use beNil() to match nils)"
return true
}
return false
}
/// Compatibility layer to new Matcher API. Converts an old-style matcher to a new one.
/// Note: You should definitely spend the time to convert to the new api as soon as possible
/// since this struct type is deprecated.
public var predicate: Predicate<T> {
return Predicate.fromDeprecatedMatcher(self)
}
}
@@ -1,154 +0,0 @@
import Foundation
// `CGFloat` is in Foundation (swift-corelibs-foundation) on Linux.
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
import CoreGraphics
#endif
/// Implement this protocol to implement a custom matcher for Swift
@available(*, deprecated, message: "Use Predicate instead")
public protocol Matcher {
associatedtype ValueType
func matches(_ actualExpression: Expression<ValueType>, failureMessage: FailureMessage) throws -> Bool
func doesNotMatch(_ actualExpression: Expression<ValueType>, failureMessage: FailureMessage) throws -> Bool
}
extension Matcher {
var predicate: Predicate<ValueType> {
return Predicate.fromDeprecatedMatcher(self)
}
var toClosure: (Expression<ValueType>, FailureMessage, Bool) throws -> Bool {
return ({ expr, msg, expectedResult in
if expectedResult {
return try self.matches(expr, failureMessage: msg)
} else {
return try self.doesNotMatch(expr, failureMessage: msg)
}
})
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
/// Objective-C interface to the Swift variant of Matcher.
@objc public protocol NMBMatcher {
func matches(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool
func doesNotMatch(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool
}
#endif
/// Protocol for types that support contain() matcher.
public protocol NMBContainer {
func contains(_ anObject: Any) -> Bool
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
// FIXME: NSHashTable can not conform to NMBContainer since swift-DEVELOPMENT-SNAPSHOT-2016-04-25-a
//extension NSHashTable : NMBContainer {} // Corelibs Foundation does not include this class yet
#endif
extension NSArray: NMBContainer {}
extension NSSet: NMBContainer {}
/// Protocol for types that support only beEmpty(), haveCount() matchers
public protocol NMBCollection {
var count: Int { get }
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NSHashTable: NMBCollection {} // Corelibs Foundation does not include these classes yet
extension NSMapTable: NMBCollection {}
#endif
extension NSSet: NMBCollection {}
extension NSIndexSet: NMBCollection {}
extension NSDictionary: NMBCollection {}
/// Protocol for types that support beginWith(), endWith(), beEmpty() matchers
public protocol NMBOrderedCollection: NMBCollection {
func object(at index: Int) -> Any
}
extension NSArray: NMBOrderedCollection {}
public protocol NMBDoubleConvertible {
var doubleValue: CDouble { get }
}
extension Double: NMBDoubleConvertible {
public var doubleValue: CDouble {
return self
}
}
extension Float: NMBDoubleConvertible {
public var doubleValue: CDouble {
return CDouble(self)
}
}
extension CGFloat: NMBDoubleConvertible {
public var doubleValue: CDouble {
return CDouble(self)
}
}
extension NSNumber: NMBDoubleConvertible {
}
private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSS"
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
extension Date: NMBDoubleConvertible {
public var doubleValue: CDouble {
return self.timeIntervalSinceReferenceDate
}
}
extension NSDate: NMBDoubleConvertible {
public var doubleValue: CDouble {
return self.timeIntervalSinceReferenceDate
}
}
extension Date: TestOutputStringConvertible {
public var testDescription: String {
return dateFormatter.string(from: self)
}
}
extension NSDate: TestOutputStringConvertible {
public var testDescription: String {
return dateFormatter.string(from: Date(timeIntervalSinceReferenceDate: self.timeIntervalSinceReferenceDate))
}
}
/// Protocol for types to support beLessThan(), beLessThanOrEqualTo(),
/// beGreaterThan(), beGreaterThanOrEqualTo(), and equal() matchers.
///
/// Types that conform to Swift's Comparable protocol will work implicitly too
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
@objc public protocol NMBComparable {
func NMB_compare(_ otherObject: NMBComparable!) -> ComparisonResult
}
#else
// This should become obsolete once Corelibs Foundation adds Comparable conformance to NSNumber
public protocol NMBComparable {
func NMB_compare(_ otherObject: NMBComparable!) -> ComparisonResult
}
#endif
extension NSNumber: NMBComparable {
public func NMB_compare(_ otherObject: NMBComparable!) -> ComparisonResult {
return compare(otherObject as! NSNumber)
}
}
extension NSString: NMBComparable {
public func NMB_compare(_ otherObject: NMBComparable!) -> ComparisonResult {
return compare(otherObject as! String)
}
}
@@ -1,98 +0,0 @@
import Foundation
// A workaround to SR-6419.
extension NotificationCenter {
#if !(os(macOS) || os(iOS) || os(tvOS) || os(watchOS))
#if swift(>=4.0)
#if swift(>=4.0.2)
#else
func addObserver(forName name: Notification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol {
return addObserver(forName: name, object: obj, queue: queue, usingBlock: block)
}
#endif
#elseif swift(>=3.2)
#if swift(>=3.2.2)
#else
// swiftlint:disable:next line_length
func addObserver(forName name: Notification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol {
return addObserver(forName: name, object: obj, queue: queue, usingBlock: block)
}
#endif
#else
// swiftlint:disable:next line_length
func addObserver(forName name: Notification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol {
return addObserver(forName: name, object: obj, queue: queue, usingBlock: block)
}
#endif
#endif
}
internal class NotificationCollector {
private(set) var observedNotifications: [Notification]
private let notificationCenter: NotificationCenter
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
private var token: AnyObject?
#else
private var token: NSObjectProtocol?
#endif
required init(notificationCenter: NotificationCenter) {
self.notificationCenter = notificationCenter
self.observedNotifications = []
}
func startObserving() {
// swiftlint:disable:next line_length
self.token = self.notificationCenter.addObserver(forName: nil, object: nil, queue: nil, using: { [weak self] n in
// linux-swift gets confused by .append(n)
self?.observedNotifications.append(n)
})
}
deinit {
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
if let token = self.token {
self.notificationCenter.removeObserver(token)
}
#else
if let token = self.token as? AnyObject {
self.notificationCenter.removeObserver(token)
}
#endif
}
}
private let mainThread = pthread_self()
public func postNotifications<T>(
_ notificationsMatcher: T,
fromNotificationCenter center: NotificationCenter = .default)
-> Predicate<Any>
where T: Matcher, T.ValueType == [Notification]
{
_ = mainThread // Force lazy-loading of this value
let collector = NotificationCollector(notificationCenter: center)
collector.startObserving()
var once: Bool = false
return Predicate { actualExpression in
let collectorNotificationsExpression = Expression(memoizedExpression: { _ in
return collector.observedNotifications
}, location: actualExpression.location, withoutCaching: true)
assert(pthread_equal(mainThread, pthread_self()) != 0, "Only expecting closure to be evaluated on main thread.")
if !once {
once = true
_ = try actualExpression.evaluate()
}
let failureMessage = FailureMessage()
let match = try notificationsMatcher.matches(collectorNotificationsExpression, failureMessage: failureMessage)
if collector.observedNotifications.isEmpty {
failureMessage.actualValue = "no notifications"
} else {
failureMessage.actualValue = "<\(stringify(collector.observedNotifications))>"
}
return PredicateResult(bool: match, message: failureMessage.toExpectationMessage())
}
}
@@ -1,352 +0,0 @@
// New Matcher API
//
import Foundation
/// A Predicate is part of the new matcher API that provides assertions to expectations.
///
/// Given a code snippet:
///
/// expect(1).to(equal(2))
/// ^^^^^^^^
/// Called a "matcher"
///
/// A matcher consists of two parts a constructor function and the Predicate. The term Predicate
/// is used as a separate name from Matcher to help transition custom matchers to the new Nimble
/// matcher API.
///
/// The Predicate provide the heavy lifting on how to assert against a given value. Internally,
/// predicates are simple wrappers around closures to provide static type information and
/// allow composition and wrapping of existing behaviors.
public struct Predicate<T> {
fileprivate var matcher: (Expression<T>) throws -> PredicateResult
/// Constructs a predicate that knows how take a given value
public init(_ matcher: @escaping (Expression<T>) throws -> PredicateResult) {
self.matcher = matcher
}
/// Uses a predicate on a given value to see if it passes the predicate.
///
/// @param expression The value to run the predicate's logic against
/// @returns A predicate result indicate passing or failing and an associated error message.
public func satisfies(_ expression: Expression<T>) throws -> PredicateResult {
return try matcher(expression)
}
}
/// Provides convenience helpers to defining predicates
extension Predicate {
/// Like Predicate() constructor, but automatically guard against nil (actual) values
public static func define(matcher: @escaping (Expression<T>) throws -> PredicateResult) -> Predicate<T> {
return Predicate<T> { actual in
return try matcher(actual)
}.requireNonNil
}
/// Defines a predicate with a default message that can be returned in the closure
/// Also ensures the predicate's actual value cannot pass with `nil` given.
public static func define(_ msg: String, matcher: @escaping (Expression<T>, ExpectationMessage) throws -> PredicateResult) -> Predicate<T> {
return Predicate<T> { actual in
return try matcher(actual, .expectedActualValueTo(msg))
}.requireNonNil
}
/// Defines a predicate with a default message that can be returned in the closure
/// Unlike `define`, this allows nil values to succeed if the given closure chooses to.
public static func defineNilable(_ msg: String, matcher: @escaping (Expression<T>, ExpectationMessage) throws -> PredicateResult) -> Predicate<T> {
return Predicate<T> { actual in
return try matcher(actual, .expectedActualValueTo(msg))
}
}
}
extension Predicate {
/// Provides a simple predicate definition that provides no control over the predefined
/// error message.
///
/// Also ensures the predicate's actual value cannot pass with `nil` given.
public static func simple(_ msg: String, matcher: @escaping (Expression<T>) throws -> PredicateStatus) -> Predicate<T> {
return Predicate<T> { actual in
return PredicateResult(status: try matcher(actual), message: .expectedActualValueTo(msg))
}.requireNonNil
}
/// Provides a simple predicate definition that provides no control over the predefined
/// error message.
///
/// Unlike `simple`, this allows nil values to succeed if the given closure chooses to.
public static func simpleNilable(_ msg: String, matcher: @escaping (Expression<T>) throws -> PredicateStatus) -> Predicate<T> {
return Predicate<T> { actual in
return PredicateResult(status: try matcher(actual), message: .expectedActualValueTo(msg))
}
}
}
// The Expectation style intended for comparison to a PredicateStatus.
public enum ExpectationStyle {
case toMatch, toNotMatch
}
/// The value that a Predicates return to describe if the given (actual) value matches the
/// predicate.
public struct PredicateResult {
/// Status indicates if the predicate matches, does not match, or fails.
public var status: PredicateStatus
/// The error message that can be displayed if it does not match
public var message: ExpectationMessage
/// Constructs a new PredicateResult with a given status and error message
public init(status: PredicateStatus, message: ExpectationMessage) {
self.status = status
self.message = message
}
/// Shorthand to PredicateResult(status: PredicateStatus(bool: bool), message: message)
public init(bool: Bool, message: ExpectationMessage) {
self.status = PredicateStatus(bool: bool)
self.message = message
}
/// Converts the result to a boolean based on what the expectation intended
public func toBoolean(expectation style: ExpectationStyle) -> Bool {
return status.toBoolean(expectation: style)
}
}
/// PredicateStatus is a trinary that indicates if a Predicate matches a given value or not
public enum PredicateStatus {
/// Matches indicates if the predicate / matcher passes with the given value
///
/// For example, `equals(1)` returns `.matches` for `expect(1).to(equal(1))`.
case matches
/// DoesNotMatch indicates if the predicate / matcher fails with the given value, but *would*
/// succeed if the expectation was inverted.
///
/// For example, `equals(2)` returns `.doesNotMatch` for `expect(1).toNot(equal(2))`.
case doesNotMatch
/// Fail indicates the predicate will never satisfy with the given value in any case.
/// A perfect example is that most matchers fail whenever given `nil`.
///
/// Using `equal(1)` fails both `expect(nil).to(equal(1))` and `expect(nil).toNot(equal(1))`.
/// Note: Predicate's `requireNonNil` property will also provide this feature mostly for free.
/// Your predicate will still need to guard against nils, but error messaging will be
/// handled for you.
case fail
/// Converts a boolean to either .matches (if true) or .doesNotMatch (if false).
public init(bool matches: Bool) {
if matches {
self = .matches
} else {
self = .doesNotMatch
}
}
private func shouldMatch() -> Bool {
switch self {
case .matches: return true
case .doesNotMatch, .fail: return false
}
}
private func shouldNotMatch() -> Bool {
switch self {
case .doesNotMatch: return true
case .matches, .fail: return false
}
}
/// Converts the PredicateStatus result to a boolean based on what the expectation intended
internal func toBoolean(expectation style: ExpectationStyle) -> Bool {
if style == .toMatch {
return shouldMatch()
} else {
return shouldNotMatch()
}
}
}
// Backwards compatibility until Old Matcher API removal
extension Predicate: Matcher {
/// Compatibility layer for old Matcher API, deprecated
public static func fromDeprecatedFullClosure(_ matcher: @escaping (Expression<T>, FailureMessage, Bool) throws -> Bool) -> Predicate {
return Predicate { actual in
let failureMessage = FailureMessage()
let result = try matcher(actual, failureMessage, true)
return PredicateResult(
status: PredicateStatus(bool: result),
message: failureMessage.toExpectationMessage()
)
}
}
/// Compatibility layer for old Matcher API, deprecated.
/// Emulates the MatcherFunc API
public static func fromDeprecatedClosure(_ matcher: @escaping (Expression<T>, FailureMessage) throws -> Bool) -> Predicate {
return Predicate { actual in
let failureMessage = FailureMessage()
let result = try matcher(actual, failureMessage)
return PredicateResult(
status: PredicateStatus(bool: result),
message: failureMessage.toExpectationMessage()
)
}
}
/// Compatibility layer for old Matcher API, deprecated.
/// Same as calling .predicate on a MatcherFunc or NonNilMatcherFunc type.
public static func fromDeprecatedMatcher<M>(_ matcher: M) -> Predicate where M: Matcher, M.ValueType == T {
return self.fromDeprecatedFullClosure(matcher.toClosure)
}
/// Deprecated Matcher API, use satisfies(_:_) instead
public func matches(_ actualExpression: Expression<T>, failureMessage: FailureMessage) throws -> Bool {
let result = try satisfies(actualExpression)
result.message.update(failureMessage: failureMessage)
return result.toBoolean(expectation: .toMatch)
}
/// Deprecated Matcher API, use satisfies(_:_) instead
public func doesNotMatch(_ actualExpression: Expression<T>, failureMessage: FailureMessage) throws -> Bool {
let result = try satisfies(actualExpression)
result.message.update(failureMessage: failureMessage)
return result.toBoolean(expectation: .toNotMatch)
}
}
extension Predicate {
// Someday, make this public? Needs documentation
internal func after(f: @escaping (Expression<T>, PredicateResult) throws -> PredicateResult) -> Predicate<T> {
return Predicate { actual -> PredicateResult in
let result = try self.satisfies(actual)
return try f(actual, result)
}
}
/// Returns a new Predicate based on the current one that always fails if nil is given as
/// the actual value.
///
/// This replaces `NonNilMatcherFunc`.
public var requireNonNil: Predicate<T> {
return after { actual, result in
if try actual.evaluate() == nil {
return PredicateResult(
status: .fail,
message: result.message.appendedBeNilHint()
)
}
return result
}
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
public typealias PredicateBlock = (_ actualExpression: Expression<NSObject>) throws -> NMBPredicateResult
public class NMBPredicate: NSObject {
private let predicate: PredicateBlock
public init(predicate: @escaping PredicateBlock) {
self.predicate = predicate
}
func satisfies(_ expression: @escaping () throws -> NSObject?, location: SourceLocation) -> NMBPredicateResult {
let expr = Expression(expression: expression, location: location)
do {
return try self.predicate(expr)
} catch let error {
return PredicateResult(status: .fail, message: .fail("unexpected error thrown: <\(error)>")).toObjectiveC()
}
}
}
extension NMBPredicate: NMBMatcher {
public func matches(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
let result = satisfies(actualBlock, location: location).toSwift()
result.message.update(failureMessage: failureMessage)
return result.status.toBoolean(expectation: .toMatch)
}
public func doesNotMatch(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
let result = satisfies(actualBlock, location: location).toSwift()
result.message.update(failureMessage: failureMessage)
return result.status.toBoolean(expectation: .toNotMatch)
}
}
final public class NMBPredicateResult: NSObject {
public var status: NMBPredicateStatus
public var message: NMBExpectationMessage
public init(status: NMBPredicateStatus, message: NMBExpectationMessage) {
self.status = status
self.message = message
}
public init(bool success: Bool, message: NMBExpectationMessage) {
self.status = NMBPredicateStatus.from(bool: success)
self.message = message
}
public func toSwift() -> PredicateResult {
return PredicateResult(status: status.toSwift(),
message: message.toSwift())
}
}
extension PredicateResult {
public func toObjectiveC() -> NMBPredicateResult {
return NMBPredicateResult(status: status.toObjectiveC(), message: message.toObjectiveC())
}
}
final public class NMBPredicateStatus: NSObject {
private let status: Int
private init(status: Int) {
self.status = status
}
public static let matches: NMBPredicateStatus = NMBPredicateStatus(status: 0)
public static let doesNotMatch: NMBPredicateStatus = NMBPredicateStatus(status: 1)
public static let fail: NMBPredicateStatus = NMBPredicateStatus(status: 2)
public override var hashValue: Int { return self.status.hashValue }
public override func isEqual(_ object: Any?) -> Bool {
guard let otherPredicate = object as? NMBPredicateStatus else {
return false
}
return self.status == otherPredicate.status
}
public static func from(status: PredicateStatus) -> NMBPredicateStatus {
switch status {
case .matches: return self.matches
case .doesNotMatch: return self.doesNotMatch
case .fail: return self.fail
}
}
public static func from(bool success: Bool) -> NMBPredicateStatus {
return self.from(status: PredicateStatus(bool: success))
}
public func toSwift() -> PredicateStatus {
switch status {
case NMBPredicateStatus.matches.status: return .matches
case NMBPredicateStatus.doesNotMatch.status: return .doesNotMatch
case NMBPredicateStatus.fail.status: return .fail
default:
internalError("Unhandle status for NMBPredicateStatus")
}
}
}
extension PredicateStatus {
public func toObjectiveC() -> NMBPredicateStatus {
return NMBPredicateStatus.from(status: self)
}
}
#endif
@@ -1,204 +0,0 @@
import Foundation
// This matcher requires the Objective-C, and being built by Xcode rather than the Swift Package Manager
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
/// A Nimble matcher that succeeds when the actual expression raises an
/// exception with the specified name, reason, and/or userInfo.
///
/// Alternatively, you can pass a closure to do any arbitrary custom matching
/// to the raised exception. The closure only gets called when an exception
/// is raised.
///
/// nil arguments indicates that the matcher should not attempt to match against
/// that parameter.
public func raiseException(
named: String? = nil,
reason: String? = nil,
userInfo: NSDictionary? = nil,
closure: ((NSException) -> Void)? = nil) -> Predicate<Any> {
return Predicate { actualExpression in
var exception: NSException?
let capture = NMBExceptionCapture(handler: ({ e in
exception = e
}), finally: nil)
capture.tryBlock {
_ = try! actualExpression.evaluate()
}
let failureMessage = FailureMessage()
setFailureMessageForException(
failureMessage,
exception: exception,
named: named,
reason: reason,
userInfo: userInfo,
closure: closure
)
let matches = exceptionMatchesNonNilFieldsOrClosure(
exception,
named: named,
reason: reason,
userInfo: userInfo,
closure: closure
)
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
// swiftlint:disable:next function_parameter_count
internal func setFailureMessageForException(
_ failureMessage: FailureMessage,
exception: NSException?,
named: String?,
reason: String?,
userInfo: NSDictionary?,
closure: ((NSException) -> Void)?) {
failureMessage.postfixMessage = "raise exception"
if let named = named {
failureMessage.postfixMessage += " with name <\(named)>"
}
if let reason = reason {
failureMessage.postfixMessage += " with reason <\(reason)>"
}
if let userInfo = userInfo {
failureMessage.postfixMessage += " with userInfo <\(userInfo)>"
}
if closure != nil {
failureMessage.postfixMessage += " that satisfies block"
}
if named == nil && reason == nil && userInfo == nil && closure == nil {
failureMessage.postfixMessage = "raise any exception"
}
if let exception = exception {
// swiftlint:disable:next line_length
failureMessage.actualValue = "\(String(describing: type(of: exception))) { name=\(exception.name), reason='\(stringify(exception.reason))', userInfo=\(stringify(exception.userInfo)) }"
} else {
failureMessage.actualValue = "no exception"
}
}
internal func exceptionMatchesNonNilFieldsOrClosure(
_ exception: NSException?,
named: String?,
reason: String?,
userInfo: NSDictionary?,
closure: ((NSException) -> Void)?) -> Bool {
var matches = false
if let exception = exception {
matches = true
if let named = named, exception.name.rawValue != named {
matches = false
}
if reason != nil && exception.reason != reason {
matches = false
}
if let userInfo = userInfo, let exceptionUserInfo = exception.userInfo,
(exceptionUserInfo as NSDictionary) != userInfo {
matches = false
}
if let closure = closure {
let assertions = gatherFailingExpectations {
closure(exception)
}
let messages = assertions.map { $0.message }
if messages.count > 0 {
matches = false
}
}
}
return matches
}
public class NMBObjCRaiseExceptionMatcher: NSObject, NMBMatcher {
internal var _name: String?
internal var _reason: String?
internal var _userInfo: NSDictionary?
internal var _block: ((NSException) -> Void)?
internal init(name: String?, reason: String?, userInfo: NSDictionary?, block: ((NSException) -> Void)?) {
_name = name
_reason = reason
_userInfo = userInfo
_block = block
}
@objc public func matches(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
let block: () -> Any? = ({ _ = actualBlock(); return nil })
let expr = Expression(expression: block, location: location)
do {
return try raiseException(
named: _name,
reason: _reason,
userInfo: _userInfo,
closure: _block
).matches(expr, failureMessage: failureMessage)
} catch let error {
failureMessage.stringValue = "unexpected error thrown: <\(error)>"
return false
}
}
@objc public func doesNotMatch(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
return !matches(actualBlock, failureMessage: failureMessage, location: location)
}
@objc public var named: (_ name: String) -> NMBObjCRaiseExceptionMatcher {
return ({ name in
return NMBObjCRaiseExceptionMatcher(
name: name,
reason: self._reason,
userInfo: self._userInfo,
block: self._block
)
})
}
@objc public var reason: (_ reason: String?) -> NMBObjCRaiseExceptionMatcher {
return ({ reason in
return NMBObjCRaiseExceptionMatcher(
name: self._name,
reason: reason,
userInfo: self._userInfo,
block: self._block
)
})
}
@objc public var userInfo: (_ userInfo: NSDictionary?) -> NMBObjCRaiseExceptionMatcher {
return ({ userInfo in
return NMBObjCRaiseExceptionMatcher(
name: self._name,
reason: self._reason,
userInfo: userInfo,
block: self._block
)
})
}
@objc public var satisfyingBlock: (_ block: ((NSException) -> Void)?) -> NMBObjCRaiseExceptionMatcher {
return ({ block in
return NMBObjCRaiseExceptionMatcher(
name: self._name,
reason: self._reason,
userInfo: self._userInfo,
block: block
)
})
}
}
extension NMBObjCMatcher {
@objc public class func raiseExceptionMatcher() -> NMBObjCRaiseExceptionMatcher {
return NMBObjCRaiseExceptionMatcher(name: nil, reason: nil, userInfo: nil, block: nil)
}
}
#endif
@@ -1,76 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value matches with all of the matchers
/// provided in the variable list of matchers.
public func satisfyAllOf<T, U>(_ matchers: U...) -> Predicate<T>
where U: Matcher, U.ValueType == T {
return satisfyAllOf(matchers.map { $0.predicate })
}
internal func satisfyAllOf<T>(_ predicates: [Predicate<T>]) -> Predicate<T> {
return Predicate.define { actualExpression in
var postfixMessages = [String]()
var matches = true
for predicate in predicates {
let result = try predicate.satisfies(actualExpression)
if result.toBoolean(expectation: .toNotMatch) {
matches = false
}
postfixMessages.append("{\(result.message.expectedMessage)}")
}
var msg: ExpectationMessage
if let actualValue = try actualExpression.evaluate() {
msg = .expectedCustomValueTo(
"match all of: " + postfixMessages.joined(separator: ", and "),
"\(actualValue)"
)
} else {
msg = .expectedActualValueTo(
"match all of: " + postfixMessages.joined(separator: ", and ")
)
}
return PredicateResult(bool: matches, message: msg)
}
}
public func && <T>(left: Predicate<T>, right: Predicate<T>) -> Predicate<T> {
return satisfyAllOf(left, right)
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func satisfyAllOfMatcher(_ matchers: [NMBMatcher]) -> NMBPredicate {
return NMBPredicate { actualExpression in
if matchers.isEmpty {
return NMBPredicateResult(
status: NMBPredicateStatus.fail,
message: NMBExpectationMessage(
fail: "satisfyAllOf must be called with at least one matcher"
)
)
}
var elementEvaluators = [Predicate<NSObject>]()
for matcher in matchers {
let elementEvaluator = Predicate<NSObject> { expression in
if let predicate = matcher as? NMBPredicate {
// swiftlint:disable:next line_length
return predicate.satisfies({ try expression.evaluate() }, location: actualExpression.location).toSwift()
} else {
let failureMessage = FailureMessage()
// swiftlint:disable:next line_length
let success = matcher.matches({ try! expression.evaluate() }, failureMessage: failureMessage, location: actualExpression.location)
return PredicateResult(bool: success, message: failureMessage.toExpectationMessage())
}
}
elementEvaluators.append(elementEvaluator)
}
return try satisfyAllOf(elementEvaluators).satisfies(actualExpression).toObjectiveC()
}
}
}
#endif
@@ -1,84 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual value matches with any of the matchers
/// provided in the variable list of matchers.
public func satisfyAnyOf<T, U>(_ matchers: U...) -> Predicate<T>
where U: Matcher, U.ValueType == T {
return satisfyAnyOf(matchers.map { $0.predicate })
}
internal func satisfyAnyOf<T>(_ predicates: [Predicate<T>]) -> Predicate<T> {
return Predicate.define { actualExpression in
var postfixMessages = [String]()
var matches = false
for predicate in predicates {
let result = try predicate.satisfies(actualExpression)
if result.toBoolean(expectation: .toMatch) {
matches = true
}
postfixMessages.append("{\(result.message.expectedMessage)}")
}
var msg: ExpectationMessage
if let actualValue = try actualExpression.evaluate() {
msg = .expectedCustomValueTo(
"match one of: " + postfixMessages.joined(separator: ", or "),
"\(actualValue)"
)
} else {
msg = .expectedActualValueTo(
"match one of: " + postfixMessages.joined(separator: ", or ")
)
}
return PredicateResult(bool: matches, message: msg)
}
}
public func || <T>(left: Predicate<T>, right: Predicate<T>) -> Predicate<T> {
return satisfyAnyOf(left, right)
}
public func || <T>(left: NonNilMatcherFunc<T>, right: NonNilMatcherFunc<T>) -> Predicate<T> {
return satisfyAnyOf(left, right)
}
public func || <T>(left: MatcherFunc<T>, right: MatcherFunc<T>) -> Predicate<T> {
return satisfyAnyOf(left, right)
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func satisfyAnyOfMatcher(_ matchers: [NMBMatcher]) -> NMBPredicate {
return NMBPredicate { actualExpression in
if matchers.isEmpty {
return NMBPredicateResult(
status: NMBPredicateStatus.fail,
message: NMBExpectationMessage(
fail: "satisfyAnyOf must be called with at least one matcher"
)
)
}
var elementEvaluators = [Predicate<NSObject>]()
for matcher in matchers {
let elementEvaluator = Predicate<NSObject> { expression in
if let predicate = matcher as? NMBPredicate {
// swiftlint:disable:next line_length
return predicate.satisfies({ try expression.evaluate() }, location: actualExpression.location).toSwift()
} else {
let failureMessage = FailureMessage()
// swiftlint:disable:next line_length
let success = matcher.matches({ try! expression.evaluate() }, failureMessage: failureMessage, location: actualExpression.location)
return PredicateResult(bool: success, message: failureMessage.toExpectationMessage())
}
}
elementEvaluators.append(elementEvaluator)
}
return try satisfyAnyOf(elementEvaluators).satisfies(actualExpression).toObjectiveC()
}
}
}
#endif
@@ -1,52 +0,0 @@
import Foundation
public func throwAssertion() -> Predicate<Void> {
return Predicate { actualExpression in
#if arch(x86_64) && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
let message = ExpectationMessage.expectedTo("throw an assertion")
var actualError: Error?
let caughtException: BadInstructionException? = catchBadInstruction {
#if os(tvOS)
if !NimbleEnvironment.activeInstance.suppressTVOSAssertionWarning {
print()
print("[Nimble Warning]: If you're getting stuck on a debugger breakpoint for a " +
"fatal error while using throwAssertion(), please disable 'Debug Executable' " +
"in your scheme. Go to 'Edit Scheme > Test > Info' and uncheck " +
"'Debug Executable'. If you've already done that, suppress this warning " +
"by setting `NimbleEnvironment.activeInstance.suppressTVOSAssertionWarning = true`. " +
"This is required because the standard methods of catching assertions " +
"(mach APIs) are unavailable for tvOS. Instead, the same mechanism the " +
"debugger uses is the fallback method for tvOS."
)
print()
NimbleEnvironment.activeInstance.suppressTVOSAssertionWarning = true
}
#endif
do {
try actualExpression.evaluate()
} catch {
actualError = error
}
}
if let actualError = actualError {
return PredicateResult(
bool: false,
message: message.appended(message: "; threw error instead <\(actualError)>")
)
} else {
return PredicateResult(bool: caughtException != nil, message: message)
}
#elseif SWIFT_PACKAGE
fatalError("The throwAssertion Nimble matcher does not currently support Swift CLI." +
" You can silence this error by placing the test case inside an #if !SWIFT_PACKAGE" +
" conditional statement")
#else
fatalError("The throwAssertion Nimble matcher can only run on x86_64 platforms with " +
"Objective-C (e.g. Mac, iPhone 5s or later simulators). You can silence this error " +
"by placing the test case inside an #if arch(x86_64) or (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) conditional statement")
// swiftlint:disable:previous line_length
#endif
}
}
@@ -1,264 +0,0 @@
import Foundation
/// A Nimble matcher that succeeds when the actual expression throws an
/// error of the specified type or from the specified case.
///
/// Errors are tried to be compared by their implementation of Equatable,
/// otherwise they fallback to comparison by _domain and _code.
///
/// Alternatively, you can pass a closure to do any arbitrary custom matching
/// to the thrown error. The closure only gets called when an error was thrown.
///
/// nil arguments indicates that the matcher should not attempt to match against
/// that parameter.
public func throwError() -> Predicate<Any> {
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch {
actualError = error
}
if let actualError = actualError {
return PredicateResult(bool: true, message: .expectedCustomValueTo("throw any error", "<\(actualError)>"))
} else {
return PredicateResult(bool: false, message: .expectedCustomValueTo("throw any error", "no error"))
}
}
}
/// A Nimble matcher that succeeds when the actual expression throws an
/// error of the specified type or from the specified case.
///
/// Errors are tried to be compared by their implementation of Equatable,
/// otherwise they fallback to comparision by _domain and _code.
///
/// Alternatively, you can pass a closure to do any arbitrary custom matching
/// to the thrown error. The closure only gets called when an error was thrown.
///
/// nil arguments indicates that the matcher should not attempt to match against
/// that parameter.
public func throwError<T: Error>(_ error: T, closure: ((Error) -> Void)? = nil) -> Predicate<Any> {
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch {
actualError = error
}
let failureMessage = FailureMessage()
setFailureMessageForError(
failureMessage,
actualError: actualError,
error: error,
errorType: nil,
closure: closure
)
var matches = false
if let actualError = actualError, errorMatchesExpectedError(actualError, expectedError: error) {
matches = true
if let closure = closure {
let assertions = gatherFailingExpectations {
closure(actualError)
}
let messages = assertions.map { $0.message }
if !messages.isEmpty {
matches = false
}
}
}
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
/// A Nimble matcher that succeeds when the actual expression throws an
/// error of the specified type or from the specified case.
///
/// Errors are tried to be compared by their implementation of Equatable,
/// otherwise they fallback to comparision by _domain and _code.
///
/// Alternatively, you can pass a closure to do any arbitrary custom matching
/// to the thrown error. The closure only gets called when an error was thrown.
///
/// nil arguments indicates that the matcher should not attempt to match against
/// that parameter.
public func throwError<T: Error & Equatable>(_ error: T, closure: ((T) -> Void)? = nil) -> Predicate<Any> {
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch {
actualError = error
}
let failureMessage = FailureMessage()
setFailureMessageForError(
failureMessage,
actualError: actualError,
error: error,
errorType: nil,
closure: closure
)
var matches = false
if let actualError = actualError as? T, error == actualError {
matches = true
if let closure = closure {
let assertions = gatherFailingExpectations {
closure(actualError)
}
let messages = assertions.map { $0.message }
if !messages.isEmpty {
matches = false
}
}
}
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
/// A Nimble matcher that succeeds when the actual expression throws an
/// error of the specified type or from the specified case.
///
/// Errors are tried to be compared by their implementation of Equatable,
/// otherwise they fallback to comparision by _domain and _code.
///
/// Alternatively, you can pass a closure to do any arbitrary custom matching
/// to the thrown error. The closure only gets called when an error was thrown.
///
/// nil arguments indicates that the matcher should not attempt to match against
/// that parameter.
public func throwError<T: Error>(
errorType: T.Type,
closure: ((T) -> Void)? = nil) -> Predicate<Any> {
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch {
actualError = error
}
let failureMessage = FailureMessage()
setFailureMessageForError(
failureMessage,
actualError: actualError,
error: nil,
errorType: errorType,
closure: closure
)
var matches = false
if let actualError = actualError {
matches = true
if let actualError = actualError as? T {
if let closure = closure {
let assertions = gatherFailingExpectations {
closure(actualError)
}
let messages = assertions.map { $0.message }
if !messages.isEmpty {
matches = false
}
}
} else {
matches = (actualError is T)
// The closure expects another ErrorProtocol as argument, so this
// is _supposed_ to fail, so that it becomes more obvious.
if let closure = closure {
let assertions = gatherExpectations {
if let actual = actualError as? T {
closure(actual)
}
}
let messages = assertions.map { $0.message }
if !messages.isEmpty {
matches = false
}
}
}
}
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
/// A Nimble matcher that succeeds when the actual expression throws any
/// error or when the passed closures' arbitrary custom matching succeeds.
///
/// This duplication to it's generic adequate is required to allow to receive
/// values of the existential type `Error` in the closure.
///
/// The closure only gets called when an error was thrown.
public func throwError(closure: @escaping ((Error) -> Void)) -> Predicate<Any> {
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch {
actualError = error
}
let failureMessage = FailureMessage()
setFailureMessageForError(failureMessage, actualError: actualError, closure: closure)
var matches = false
if let actualError = actualError {
matches = true
let assertions = gatherFailingExpectations {
closure(actualError)
}
let messages = assertions.map { $0.message }
if !messages.isEmpty {
matches = false
}
}
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
/// A Nimble matcher that succeeds when the actual expression throws any
/// error or when the passed closures' arbitrary custom matching succeeds.
///
/// This duplication to it's generic adequate is required to allow to receive
/// values of the existential type `Error` in the closure.
///
/// The closure only gets called when an error was thrown.
public func throwError<T: Error>(closure: @escaping ((T) -> Void)) -> Predicate<Any> {
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch {
actualError = error
}
let failureMessage = FailureMessage()
setFailureMessageForError(failureMessage, actualError: actualError, closure: closure)
var matches = false
if let actualError = actualError as? T {
matches = true
let assertions = gatherFailingExpectations {
closure(actualError)
}
let messages = assertions.map { $0.message }
if !messages.isEmpty {
matches = false
}
}
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
@@ -1,37 +0,0 @@
/**
Used by the `toSucceed` matcher.
This is the return type for the closure.
*/
public enum ToSucceedResult {
case succeeded
case failed(reason: String)
}
/**
A Nimble matcher that takes in a closure for validation.
Return `.succeeded` when the validation succeeds.
Return `.failed` with a failure reason when the validation fails.
*/
public func succeed() -> Predicate<() -> ToSucceedResult> {
return Predicate.define { actualExpression in
let optActual = try actualExpression.evaluate()
guard let actual = optActual else {
return PredicateResult(status: .fail, message: .fail("expected a closure, got <nil>"))
}
switch actual() {
case .succeeded:
return PredicateResult(
bool: true,
message: .expectedCustomValueTo("succeed", "<succeeded>")
)
case .failed(let reason):
return PredicateResult(
bool: false,
message: .expectedCustomValueTo("succeed", "<failed> because <\(reason)>")
)
}
}
}
-13
View File
@@ -1,13 +0,0 @@
#import <Foundation/Foundation.h>
#import "NMBExceptionCapture.h"
#import "NMBStringify.h"
#import "DSL.h"
#if TARGET_OS_TV
#import "CwlPreconditionTesting_POSIX.h"
#else
#import "CwlPreconditionTesting.h"
#endif
FOUNDATION_EXPORT double NimbleVersionNumber;
FOUNDATION_EXPORT const unsigned char NimbleVersionString[];
-381
View File
@@ -1,381 +0,0 @@
import CoreFoundation
import Dispatch
import Foundation
#if !(os(macOS) || os(iOS) || os(tvOS) || os(watchOS))
import CDispatch
#endif
private let timeoutLeeway = DispatchTimeInterval.milliseconds(1)
private let pollLeeway = DispatchTimeInterval.milliseconds(1)
/// Stores debugging information about callers
internal struct WaitingInfo: CustomStringConvertible {
let name: String
let file: FileString
let lineNumber: UInt
var description: String {
return "\(name) at \(file):\(lineNumber)"
}
}
internal protocol WaitLock {
func acquireWaitingLock(_ fnName: String, file: FileString, line: UInt)
func releaseWaitingLock()
func isWaitingLocked() -> Bool
}
internal class AssertionWaitLock: WaitLock {
private var currentWaiter: WaitingInfo?
init() { }
func acquireWaitingLock(_ fnName: String, file: FileString, line: UInt) {
let info = WaitingInfo(name: fnName, file: file, lineNumber: line)
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let isMainThread = Thread.isMainThread
#else
let isMainThread = _CFIsMainThread()
#endif
nimblePrecondition(
isMainThread,
"InvalidNimbleAPIUsage",
"\(fnName) can only run on the main thread."
)
nimblePrecondition(
currentWaiter == nil,
"InvalidNimbleAPIUsage",
"Nested async expectations are not allowed to avoid creating flaky tests.\n\n" +
"The call to\n\t\(info)\n" +
"triggered this exception because\n\t\(currentWaiter!)\n" +
"is currently managing the main run loop."
)
currentWaiter = info
}
func isWaitingLocked() -> Bool {
return currentWaiter != nil
}
func releaseWaitingLock() {
currentWaiter = nil
}
}
internal enum AwaitResult<T> {
/// Incomplete indicates None (aka - this value hasn't been fulfilled yet)
case incomplete
/// TimedOut indicates the result reached its defined timeout limit before returning
case timedOut
/// BlockedRunLoop indicates the main runloop is too busy processing other blocks to trigger
/// the timeout code.
///
/// This may also mean the async code waiting upon may have never actually ran within the
/// required time because other timers & sources are running on the main run loop.
case blockedRunLoop
/// The async block successfully executed and returned a given result
case completed(T)
/// When a Swift Error is thrown
case errorThrown(Error)
/// When an Objective-C Exception is raised
case raisedException(NSException)
func isIncomplete() -> Bool {
switch self {
case .incomplete: return true
default: return false
}
}
func isCompleted() -> Bool {
switch self {
case .completed: return true
default: return false
}
}
}
/// Holds the resulting value from an asynchronous expectation.
/// This class is thread-safe at receiving an "response" to this promise.
internal class AwaitPromise<T> {
private(set) internal var asyncResult: AwaitResult<T> = .incomplete
private var signal: DispatchSemaphore
init() {
signal = DispatchSemaphore(value: 1)
}
deinit {
signal.signal()
}
/// Resolves the promise with the given result if it has not been resolved. Repeated calls to
/// this method will resolve in a no-op.
///
/// @returns a Bool that indicates if the async result was accepted or rejected because another
/// value was received first.
func resolveResult(_ result: AwaitResult<T>) -> Bool {
if signal.wait(timeout: .now()) == .success {
self.asyncResult = result
return true
} else {
return false
}
}
}
internal struct AwaitTrigger {
let timeoutSource: DispatchSourceTimer
let actionSource: DispatchSourceTimer?
let start: () throws -> Void
}
/// Factory for building fully configured AwaitPromises and waiting for their results.
///
/// This factory stores all the state for an async expectation so that Await doesn't
/// doesn't have to manage it.
internal class AwaitPromiseBuilder<T> {
let awaiter: Awaiter
let waitLock: WaitLock
let trigger: AwaitTrigger
let promise: AwaitPromise<T>
internal init(
awaiter: Awaiter,
waitLock: WaitLock,
promise: AwaitPromise<T>,
trigger: AwaitTrigger) {
self.awaiter = awaiter
self.waitLock = waitLock
self.promise = promise
self.trigger = trigger
}
func timeout(_ timeoutInterval: TimeInterval, forcefullyAbortTimeout: TimeInterval) -> Self {
// = Discussion =
//
// There's a lot of technical decisions here that is useful to elaborate on. This is
// definitely more lower-level than the previous NSRunLoop based implementation.
//
//
// Why Dispatch Source?
//
//
// We're using a dispatch source to have better control of the run loop behavior.
// A timer source gives us deferred-timing control without having to rely as much on
// a run loop's traditional dispatching machinery (eg - NSTimers, DefaultRunLoopMode, etc.)
// which is ripe for getting corrupted by application code.
//
// And unlike dispatch_async(), we can control how likely our code gets prioritized to
// executed (see leeway parameter) + DISPATCH_TIMER_STRICT.
//
// This timer is assumed to run on the HIGH priority queue to ensure it maintains the
// highest priority over normal application / test code when possible.
//
//
// Run Loop Management
//
// In order to properly interrupt the waiting behavior performed by this factory class,
// this timer stops the main run loop to tell the waiter code that the result should be
// checked.
//
// In addition, stopping the run loop is used to halt code executed on the main run loop.
#if swift(>=4.0)
trigger.timeoutSource.schedule(
deadline: DispatchTime.now() + timeoutInterval,
repeating: .never,
leeway: timeoutLeeway
)
#else
trigger.timeoutSource.scheduleOneshot(
deadline: DispatchTime.now() + timeoutInterval,
leeway: timeoutLeeway
)
#endif
trigger.timeoutSource.setEventHandler {
guard self.promise.asyncResult.isIncomplete() else { return }
let timedOutSem = DispatchSemaphore(value: 0)
let semTimedOutOrBlocked = DispatchSemaphore(value: 0)
semTimedOutOrBlocked.signal()
let runLoop = CFRunLoopGetMain()
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let runLoopMode = CFRunLoopMode.defaultMode.rawValue
#else
let runLoopMode = kCFRunLoopDefaultMode
#endif
CFRunLoopPerformBlock(runLoop, runLoopMode) {
if semTimedOutOrBlocked.wait(timeout: .now()) == .success {
timedOutSem.signal()
semTimedOutOrBlocked.signal()
if self.promise.resolveResult(.timedOut) {
CFRunLoopStop(CFRunLoopGetMain())
}
}
}
// potentially interrupt blocking code on run loop to let timeout code run
CFRunLoopStop(runLoop)
let now = DispatchTime.now() + forcefullyAbortTimeout
let didNotTimeOut = timedOutSem.wait(timeout: now) != .success
let timeoutWasNotTriggered = semTimedOutOrBlocked.wait(timeout: .now()) == .success
if didNotTimeOut && timeoutWasNotTriggered {
if self.promise.resolveResult(.blockedRunLoop) {
CFRunLoopStop(CFRunLoopGetMain())
}
}
}
return self
}
/// Blocks for an asynchronous result.
///
/// @discussion
/// This function must be executed on the main thread and cannot be nested. This is because
/// this function (and it's related methods) coordinate through the main run loop. Tampering
/// with the run loop can cause undesirable behavior.
///
/// This method will return an AwaitResult in the following cases:
///
/// - The main run loop is blocked by other operations and the async expectation cannot be
/// be stopped.
/// - The async expectation timed out
/// - The async expectation succeeded
/// - The async expectation raised an unexpected exception (objc)
/// - The async expectation raised an unexpected error (swift)
///
/// The returned AwaitResult will NEVER be .incomplete.
func wait(_ fnName: String = #function, file: FileString = #file, line: UInt = #line) -> AwaitResult<T> {
waitLock.acquireWaitingLock(
fnName,
file: file,
line: line)
let capture = NMBExceptionCapture(handler: ({ exception in
_ = self.promise.resolveResult(.raisedException(exception))
}), finally: ({
self.waitLock.releaseWaitingLock()
}))
capture.tryBlock {
do {
try self.trigger.start()
} catch let error {
_ = self.promise.resolveResult(.errorThrown(error))
}
self.trigger.timeoutSource.resume()
while self.promise.asyncResult.isIncomplete() {
// Stopping the run loop does not work unless we run only 1 mode
#if swift(>=4.2)
_ = RunLoop.current.run(mode: .default, before: .distantFuture)
#else
_ = RunLoop.current.run(mode: .defaultRunLoopMode, before: .distantFuture)
#endif
}
self.trigger.timeoutSource.cancel()
if let asyncSource = self.trigger.actionSource {
asyncSource.cancel()
}
}
return promise.asyncResult
}
}
internal class Awaiter {
let waitLock: WaitLock
let timeoutQueue: DispatchQueue
let asyncQueue: DispatchQueue
internal init(
waitLock: WaitLock,
asyncQueue: DispatchQueue,
timeoutQueue: DispatchQueue) {
self.waitLock = waitLock
self.asyncQueue = asyncQueue
self.timeoutQueue = timeoutQueue
}
private func createTimerSource(_ queue: DispatchQueue) -> DispatchSourceTimer {
return DispatchSource.makeTimerSource(flags: .strict, queue: queue)
}
func performBlock<T>(
file: FileString,
line: UInt,
_ closure: @escaping (@escaping (T) -> Void) throws -> Void
) -> AwaitPromiseBuilder<T> {
let promise = AwaitPromise<T>()
let timeoutSource = createTimerSource(timeoutQueue)
var completionCount = 0
let trigger = AwaitTrigger(timeoutSource: timeoutSource, actionSource: nil) {
try closure {
completionCount += 1
if completionCount < 2 {
if promise.resolveResult(.completed($0)) {
CFRunLoopStop(CFRunLoopGetMain())
}
} else {
fail("waitUntil(..) expects its completion closure to be only called once",
file: file, line: line)
}
}
}
return AwaitPromiseBuilder(
awaiter: self,
waitLock: waitLock,
promise: promise,
trigger: trigger)
}
func poll<T>(_ pollInterval: TimeInterval, closure: @escaping () throws -> T?) -> AwaitPromiseBuilder<T> {
let promise = AwaitPromise<T>()
let timeoutSource = createTimerSource(timeoutQueue)
let asyncSource = createTimerSource(asyncQueue)
let trigger = AwaitTrigger(timeoutSource: timeoutSource, actionSource: asyncSource) {
let interval = DispatchTimeInterval.nanoseconds(Int(pollInterval * TimeInterval(NSEC_PER_SEC)))
#if swift(>=4.0)
asyncSource.schedule(deadline: .now(), repeating: interval, leeway: pollLeeway)
#else
asyncSource.scheduleRepeating(deadline: .now(), interval: interval, leeway: pollLeeway)
#endif
asyncSource.setEventHandler {
do {
if let result = try closure() {
if promise.resolveResult(.completed(result)) {
CFRunLoopStop(CFRunLoopGetCurrent())
}
}
} catch let error {
if promise.resolveResult(.errorThrown(error)) {
CFRunLoopStop(CFRunLoopGetCurrent())
}
}
}
asyncSource.resume()
}
return AwaitPromiseBuilder(
awaiter: self,
waitLock: waitLock,
promise: promise,
trigger: trigger)
}
}
internal func pollBlock(
pollInterval: TimeInterval,
timeoutInterval: TimeInterval,
file: FileString,
line: UInt,
fnName: String = #function,
expression: @escaping () throws -> Bool) -> AwaitResult<Bool> {
let awaiter = NimbleEnvironment.activeInstance.awaiter
let result = awaiter.poll(pollInterval) { () throws -> Bool? in
if try expression() {
return true
}
return nil
}.timeout(timeoutInterval, forcefullyAbortTimeout: timeoutInterval / 2.0).wait(fnName, file: file, line: line)
return result
}
-59
View File
@@ -1,59 +0,0 @@
import Foundation
// Generic
internal func setFailureMessageForError<T: Error>(
_ failureMessage: FailureMessage,
postfixMessageVerb: String = "throw",
actualError: Error?,
error: T? = nil,
errorType: T.Type? = nil,
closure: ((T) -> Void)? = nil) {
failureMessage.postfixMessage = "\(postfixMessageVerb) error"
if let error = error {
failureMessage.postfixMessage += " <\(error)>"
} else if errorType != nil || closure != nil {
failureMessage.postfixMessage += " from type <\(T.self)>"
}
if closure != nil {
failureMessage.postfixMessage += " that satisfies block"
}
if error == nil && errorType == nil && closure == nil {
failureMessage.postfixMessage = "\(postfixMessageVerb) any error"
}
if let actualError = actualError {
failureMessage.actualValue = "<\(actualError)>"
} else {
failureMessage.actualValue = "no error"
}
}
internal func errorMatchesExpectedError<T: Error>(
_ actualError: Error,
expectedError: T) -> Bool {
return actualError._domain == expectedError._domain
&& actualError._code == expectedError._code
}
// Non-generic
internal func setFailureMessageForError(
_ failureMessage: FailureMessage,
actualError: Error?,
closure: ((Error) -> Void)?) {
failureMessage.postfixMessage = "throw error"
if closure != nil {
failureMessage.postfixMessage += " that satisfies block"
} else {
failureMessage.postfixMessage = "throw any error"
}
if let actualError = actualError {
failureMessage.actualValue = "<\(actualError)>"
} else {
failureMessage.actualValue = "no error"
}
}
@@ -1,12 +0,0 @@
import Foundation
extension Sequence {
internal func all(_ fn: (Iterator.Element) -> Bool) -> Bool {
for item in self {
if !fn(item) {
return false
}
}
return true
}
}
@@ -1,31 +0,0 @@
import Foundation
// Ideally we would always use `StaticString` as the type for tracking the file name
// that expectations originate from, for consistency with `assert` etc. from the
// stdlib, and because recent versions of the XCTest overlay require `StaticString`
// when calling `XCTFail`. Under the Objective-C runtime (i.e. building on Mac), we
// have to use `String` instead because StaticString can't be generated from Objective-C
#if SWIFT_PACKAGE
public typealias FileString = StaticString
#else
public typealias FileString = String
#endif
public final class SourceLocation: NSObject {
public let file: FileString
public let line: UInt
override init() {
file = "Unknown File"
line = 0
}
init(file: FileString, line: UInt) {
self.file = file
self.line = line
}
override public var description: String {
return "\(file):\(line)"
}
}
-206
View File
@@ -1,206 +0,0 @@
import Foundation
internal func identityAsString(_ value: Any?) -> String {
let anyObject: AnyObject?
#if os(Linux)
anyObject = value as? AnyObject
#else
anyObject = value as AnyObject?
#endif
if let value = anyObject {
return NSString(format: "<%p>", unsafeBitCast(value, to: Int.self)).description
} else {
return "nil"
}
}
internal func arrayAsString<T>(_ items: [T], joiner: String = ", ") -> String {
return items.reduce("") { accum, item in
let prefix = (accum.isEmpty ? "" : joiner)
return accum + prefix + "\(stringify(item))"
}
}
/// A type with a customized test output text representation.
///
/// This textual representation is produced when values will be
/// printed in test runs, and may be useful when producing
/// error messages in custom matchers.
///
/// - SeeAlso: `CustomDebugStringConvertible`
public protocol TestOutputStringConvertible {
var testDescription: String { get }
}
extension Double: TestOutputStringConvertible {
public var testDescription: String {
return NSNumber(value: self).testDescription
}
}
extension Float: TestOutputStringConvertible {
public var testDescription: String {
return NSNumber(value: self).testDescription
}
}
extension NSNumber: TestOutputStringConvertible {
// This is using `NSString(format:)` instead of
// `String(format:)` because the latter somehow breaks
// the travis CI build on linux.
public var testDescription: String {
let description = self.description
if description.contains(".") {
// Travis linux swiftpm build doesn't like casting String to NSString,
// which is why this annoying nested initializer thing is here.
// Maybe this will change in a future snapshot.
let decimalPlaces = NSString(string: NSString(string: description)
.components(separatedBy: ".")[1])
// SeeAlso: https://bugs.swift.org/browse/SR-1464
switch decimalPlaces.length {
case 1:
return NSString(format: "%0.1f", self.doubleValue).description
case 2:
return NSString(format: "%0.2f", self.doubleValue).description
case 3:
return NSString(format: "%0.3f", self.doubleValue).description
default:
return NSString(format: "%0.4f", self.doubleValue).description
}
}
return self.description
}
}
extension Array: TestOutputStringConvertible {
public var testDescription: String {
let list = self.map(Nimble.stringify).joined(separator: ", ")
return "[\(list)]"
}
}
extension AnySequence: TestOutputStringConvertible {
public var testDescription: String {
let generator = self.makeIterator()
var strings = [String]()
var value: AnySequence.Iterator.Element?
repeat {
value = generator.next()
if let value = value {
strings.append(stringify(value))
}
} while value != nil
let list = strings.joined(separator: ", ")
return "[\(list)]"
}
}
extension NSArray: TestOutputStringConvertible {
public var testDescription: String {
let list = Array(self).map(Nimble.stringify).joined(separator: ", ")
return "(\(list))"
}
}
extension NSIndexSet: TestOutputStringConvertible {
public var testDescription: String {
let list = Array(self).map(Nimble.stringify).joined(separator: ", ")
return "(\(list))"
}
}
extension String: TestOutputStringConvertible {
public var testDescription: String {
return self
}
}
extension Data: TestOutputStringConvertible {
public var testDescription: String {
#if os(Linux)
// FIXME: Swift on Linux triggers a segfault when calling NSData's hash() (last checked on 03-11-16)
return "Data<length=\(count)>"
#else
return "Data<hash=\((self as NSData).hash),length=\(count)>"
#endif
}
}
///
/// Returns a string appropriate for displaying in test output
/// from the provided value.
///
/// - parameter value: A value that will show up in a test's output.
///
/// - returns: The string that is returned can be
/// customized per type by conforming a type to the `TestOutputStringConvertible`
/// protocol. When stringifying a non-`TestOutputStringConvertible` type, this
/// function will return the value's debug description and then its
/// normal description if available and in that order. Otherwise it
/// will return the result of constructing a string from the value.
///
/// - SeeAlso: `TestOutputStringConvertible`
public func stringify<T>(_ value: T?) -> String {
guard let value = value else { return "nil" }
if let value = value as? TestOutputStringConvertible {
return value.testDescription
}
if let value = value as? CustomDebugStringConvertible {
return value.debugDescription
}
return String(describing: value)
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
@objc public class NMBStringer: NSObject {
@objc public class func stringify(_ obj: Any?) -> String {
return Nimble.stringify(obj)
}
}
#endif
// MARK: Collection Type Stringers
/// Attempts to generate a pretty type string for a given value. If the value is of a Objective-C
/// collection type, or a subclass thereof, (e.g. `NSArray`, `NSDictionary`, etc.).
/// This function will return the type name of the root class of the class cluster for better
/// readability (e.g. `NSArray` instead of `__NSArrayI`).
///
/// For values that don't have a type of an Objective-C collection, this function returns the
/// default type description.
///
/// - parameter value: A value that will be used to determine a type name.
///
/// - returns: The name of the class cluster root class for Objective-C collection types, or the
/// the `dynamicType` of the value for values of any other type.
public func prettyCollectionType<T>(_ value: T) -> String {
switch value {
case is NSArray:
return String(describing: NSArray.self)
case is NSDictionary:
return String(describing: NSDictionary.self)
case is NSSet:
return String(describing: NSSet.self)
case is NSIndexSet:
return String(describing: NSIndexSet.self)
default:
return String(describing: value)
}
}
/// Returns the type name for a given collection type. This overload is used by Swift
/// collection types.
///
/// - parameter collection: A Swift `CollectionType` value.
///
/// - returns: A string representing the `dynamicType` of the value.
public func prettyCollectionType<T: Collection>(_ collection: T) -> String {
return String(describing: type(of: collection))
}
-389
View File
@@ -1,389 +0,0 @@
#import <Foundation/Foundation.h>
@class NMBExpectation;
@class NMBObjCBeCloseToMatcher;
@class NMBObjCRaiseExceptionMatcher;
@protocol NMBMatcher;
NS_ASSUME_NONNULL_BEGIN
#define NIMBLE_OVERLOADABLE __attribute__((overloadable))
#define NIMBLE_EXPORT FOUNDATION_EXPORT
#define NIMBLE_EXPORT_INLINE FOUNDATION_STATIC_INLINE
#define NIMBLE_VALUE_OF(VAL) ({ \
__typeof__((VAL)) val = (VAL); \
[NSValue valueWithBytes:&val objCType:@encode(__typeof__((VAL)))]; \
})
#ifdef NIMBLE_DISABLE_SHORT_SYNTAX
#define NIMBLE_SHORT(PROTO, ORIGINAL)
#define NIMBLE_SHORT_OVERLOADED(PROTO, ORIGINAL)
#else
#define NIMBLE_SHORT(PROTO, ORIGINAL) FOUNDATION_STATIC_INLINE PROTO { return (ORIGINAL); }
#define NIMBLE_SHORT_OVERLOADED(PROTO, ORIGINAL) FOUNDATION_STATIC_INLINE NIMBLE_OVERLOADABLE PROTO { return (ORIGINAL); }
#endif
#define DEFINE_NMB_EXPECT_OVERLOAD(TYPE, EXPR) \
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE \
NMBExpectation *NMB_expect(TYPE(^actualBlock)(void), NSString *file, NSUInteger line) { \
return NMB_expect(^id { return EXPR; }, file, line); \
}
NIMBLE_EXPORT NIMBLE_OVERLOADABLE
NMBExpectation *NMB_expect(id(^actualBlock)(void), NSString *file, NSUInteger line);
// overloaded dispatch for nils - expect(nil)
DEFINE_NMB_EXPECT_OVERLOAD(void*, nil)
DEFINE_NMB_EXPECT_OVERLOAD(NSRange, NIMBLE_VALUE_OF(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(long, @(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(unsigned long, @(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(int, @(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(unsigned int, @(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(float, @(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(double, @(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(long long, @(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(unsigned long long, @(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(char, @(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(unsigned char, @(actualBlock()))
// bool doesn't get the compiler to dispatch to BOOL types, but using BOOL here seems to allow
// the compiler to dispatch to bool.
DEFINE_NMB_EXPECT_OVERLOAD(BOOL, @(actualBlock()))
DEFINE_NMB_EXPECT_OVERLOAD(char *, @(actualBlock()))
#undef DEFINE_NMB_EXPECT_OVERLOAD
NIMBLE_EXPORT NMBExpectation *NMB_expectAction(void(^actualBlock)(void), NSString *file, NSUInteger line);
#define DEFINE_OVERLOAD(TYPE, EXPR) \
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE \
id<NMBMatcher> NMB_equal(TYPE expectedValue) { \
return NMB_equal((EXPR)); \
} \
NIMBLE_SHORT_OVERLOADED(id<NMBMatcher> equal(TYPE expectedValue), NMB_equal(expectedValue));
NIMBLE_EXPORT NIMBLE_OVERLOADABLE
id<NMBMatcher> NMB_equal(__nullable id expectedValue);
NIMBLE_SHORT_OVERLOADED(id<NMBMatcher> equal(__nullable id expectedValue),
NMB_equal(expectedValue));
// overloaded dispatch for nils - expect(nil)
DEFINE_OVERLOAD(void*__nullable, (id)nil)
DEFINE_OVERLOAD(NSRange, NIMBLE_VALUE_OF(expectedValue))
DEFINE_OVERLOAD(long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long, @(expectedValue))
DEFINE_OVERLOAD(int, @(expectedValue))
DEFINE_OVERLOAD(unsigned int, @(expectedValue))
DEFINE_OVERLOAD(float, @(expectedValue))
DEFINE_OVERLOAD(double, @(expectedValue))
DEFINE_OVERLOAD(long long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long long, @(expectedValue))
DEFINE_OVERLOAD(char, @(expectedValue))
DEFINE_OVERLOAD(unsigned char, @(expectedValue))
// bool doesn't get the compiler to dispatch to BOOL types, but using BOOL here seems to allow
// the compiler to dispatch to bool.
DEFINE_OVERLOAD(BOOL, @(expectedValue))
DEFINE_OVERLOAD(char *, @(expectedValue))
#undef DEFINE_OVERLOAD
#define DEFINE_OVERLOAD(TYPE, EXPR) \
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE \
id<NMBMatcher> NMB_haveCount(TYPE expectedValue) { \
return NMB_haveCount((EXPR)); \
} \
NIMBLE_SHORT_OVERLOADED(id<NMBMatcher> haveCount(TYPE expectedValue), \
NMB_haveCount(expectedValue));
NIMBLE_EXPORT NIMBLE_OVERLOADABLE
id<NMBMatcher> NMB_haveCount(id expectedValue);
NIMBLE_SHORT_OVERLOADED(id<NMBMatcher> haveCount(id expectedValue),
NMB_haveCount(expectedValue));
DEFINE_OVERLOAD(long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long, @(expectedValue))
DEFINE_OVERLOAD(int, @(expectedValue))
DEFINE_OVERLOAD(unsigned int, @(expectedValue))
DEFINE_OVERLOAD(long long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long long, @(expectedValue))
DEFINE_OVERLOAD(char, @(expectedValue))
DEFINE_OVERLOAD(unsigned char, @(expectedValue))
#undef DEFINE_OVERLOAD
#define DEFINE_OVERLOAD(TYPE, EXPR) \
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE \
NMBObjCBeCloseToMatcher *NMB_beCloseTo(TYPE expectedValue) { \
return NMB_beCloseTo((NSNumber *)(EXPR)); \
} \
NIMBLE_SHORT_OVERLOADED(NMBObjCBeCloseToMatcher *beCloseTo(TYPE expectedValue), \
NMB_beCloseTo(expectedValue));
NIMBLE_EXPORT NIMBLE_OVERLOADABLE NMBObjCBeCloseToMatcher *NMB_beCloseTo(NSNumber *expectedValue);
NIMBLE_SHORT_OVERLOADED(NMBObjCBeCloseToMatcher *beCloseTo(NSNumber *expectedValue),
NMB_beCloseTo(expectedValue));
// it would be better to only overload float & double, but zero becomes ambigious
DEFINE_OVERLOAD(long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long, @(expectedValue))
DEFINE_OVERLOAD(int, @(expectedValue))
DEFINE_OVERLOAD(unsigned int, @(expectedValue))
DEFINE_OVERLOAD(float, @(expectedValue))
DEFINE_OVERLOAD(double, @(expectedValue))
DEFINE_OVERLOAD(long long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long long, @(expectedValue))
DEFINE_OVERLOAD(char, @(expectedValue))
DEFINE_OVERLOAD(unsigned char, @(expectedValue))
#undef DEFINE_OVERLOAD
NIMBLE_EXPORT id<NMBMatcher> NMB_beAnInstanceOf(Class expectedClass);
NIMBLE_EXPORT_INLINE id<NMBMatcher> beAnInstanceOf(Class expectedClass) {
return NMB_beAnInstanceOf(expectedClass);
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beAKindOf(Class expectedClass);
NIMBLE_EXPORT_INLINE id<NMBMatcher> beAKindOf(Class expectedClass) {
return NMB_beAKindOf(expectedClass);
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beginWith(id itemElementOrSubstring);
NIMBLE_EXPORT_INLINE id<NMBMatcher> beginWith(id itemElementOrSubstring) {
return NMB_beginWith(itemElementOrSubstring);
}
#define DEFINE_OVERLOAD(TYPE, EXPR) \
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE \
id<NMBMatcher> NMB_beGreaterThan(TYPE expectedValue) { \
return NMB_beGreaterThan((EXPR)); \
} \
NIMBLE_SHORT_OVERLOADED(id<NMBMatcher> beGreaterThan(TYPE expectedValue), NMB_beGreaterThan(expectedValue));
NIMBLE_EXPORT NIMBLE_OVERLOADABLE
id<NMBMatcher> NMB_beGreaterThan(NSNumber *expectedValue);
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE
id<NMBMatcher> beGreaterThan(NSNumber *expectedValue) {
return NMB_beGreaterThan(expectedValue);
}
DEFINE_OVERLOAD(long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long, @(expectedValue))
DEFINE_OVERLOAD(int, @(expectedValue))
DEFINE_OVERLOAD(unsigned int, @(expectedValue))
DEFINE_OVERLOAD(float, @(expectedValue))
DEFINE_OVERLOAD(double, @(expectedValue))
DEFINE_OVERLOAD(long long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long long, @(expectedValue))
DEFINE_OVERLOAD(char, @(expectedValue))
DEFINE_OVERLOAD(unsigned char, @(expectedValue))
#undef DEFINE_OVERLOAD
#define DEFINE_OVERLOAD(TYPE, EXPR) \
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE \
id<NMBMatcher> NMB_beGreaterThanOrEqualTo(TYPE expectedValue) { \
return NMB_beGreaterThanOrEqualTo((EXPR)); \
} \
NIMBLE_SHORT_OVERLOADED(id<NMBMatcher> beGreaterThanOrEqualTo(TYPE expectedValue), \
NMB_beGreaterThanOrEqualTo(expectedValue));
NIMBLE_EXPORT NIMBLE_OVERLOADABLE
id<NMBMatcher> NMB_beGreaterThanOrEqualTo(NSNumber *expectedValue);
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE
id<NMBMatcher> beGreaterThanOrEqualTo(NSNumber *expectedValue) {
return NMB_beGreaterThanOrEqualTo(expectedValue);
}
DEFINE_OVERLOAD(long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long, @(expectedValue))
DEFINE_OVERLOAD(int, @(expectedValue))
DEFINE_OVERLOAD(unsigned int, @(expectedValue))
DEFINE_OVERLOAD(float, @(expectedValue))
DEFINE_OVERLOAD(double, @(expectedValue))
DEFINE_OVERLOAD(long long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long long, @(expectedValue))
DEFINE_OVERLOAD(char, @(expectedValue))
DEFINE_OVERLOAD(unsigned char, @(expectedValue))
#undef DEFINE_OVERLOAD
NIMBLE_EXPORT id<NMBMatcher> NMB_beIdenticalTo(id expectedInstance);
NIMBLE_SHORT(id<NMBMatcher> beIdenticalTo(id expectedInstance),
NMB_beIdenticalTo(expectedInstance));
NIMBLE_EXPORT id<NMBMatcher> NMB_be(id expectedInstance);
NIMBLE_SHORT(id<NMBMatcher> be(id expectedInstance),
NMB_be(expectedInstance));
#define DEFINE_OVERLOAD(TYPE, EXPR) \
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE \
id<NMBMatcher> NMB_beLessThan(TYPE expectedValue) { \
return NMB_beLessThan((EXPR)); \
} \
NIMBLE_SHORT_OVERLOADED(id<NMBMatcher> beLessThan(TYPE expectedValue), \
NMB_beLessThan(expectedValue));
NIMBLE_EXPORT NIMBLE_OVERLOADABLE
id<NMBMatcher> NMB_beLessThan(NSNumber *expectedValue);
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE
id<NMBMatcher> beLessThan(NSNumber *expectedValue) {
return NMB_beLessThan(expectedValue);
}
DEFINE_OVERLOAD(long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long, @(expectedValue))
DEFINE_OVERLOAD(int, @(expectedValue))
DEFINE_OVERLOAD(unsigned int, @(expectedValue))
DEFINE_OVERLOAD(float, @(expectedValue))
DEFINE_OVERLOAD(double, @(expectedValue))
DEFINE_OVERLOAD(long long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long long, @(expectedValue))
DEFINE_OVERLOAD(char, @(expectedValue))
DEFINE_OVERLOAD(unsigned char, @(expectedValue))
#undef DEFINE_OVERLOAD
#define DEFINE_OVERLOAD(TYPE, EXPR) \
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE \
id<NMBMatcher> NMB_beLessThanOrEqualTo(TYPE expectedValue) { \
return NMB_beLessThanOrEqualTo((EXPR)); \
} \
NIMBLE_SHORT_OVERLOADED(id<NMBMatcher> beLessThanOrEqualTo(TYPE expectedValue), \
NMB_beLessThanOrEqualTo(expectedValue));
NIMBLE_EXPORT NIMBLE_OVERLOADABLE
id<NMBMatcher> NMB_beLessThanOrEqualTo(NSNumber *expectedValue);
NIMBLE_EXPORT_INLINE NIMBLE_OVERLOADABLE
id<NMBMatcher> beLessThanOrEqualTo(NSNumber *expectedValue) {
return NMB_beLessThanOrEqualTo(expectedValue);
}
DEFINE_OVERLOAD(long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long, @(expectedValue))
DEFINE_OVERLOAD(int, @(expectedValue))
DEFINE_OVERLOAD(unsigned int, @(expectedValue))
DEFINE_OVERLOAD(float, @(expectedValue))
DEFINE_OVERLOAD(double, @(expectedValue))
DEFINE_OVERLOAD(long long, @(expectedValue))
DEFINE_OVERLOAD(unsigned long long, @(expectedValue))
DEFINE_OVERLOAD(char, @(expectedValue))
DEFINE_OVERLOAD(unsigned char, @(expectedValue))
#undef DEFINE_OVERLOAD
NIMBLE_EXPORT id<NMBMatcher> NMB_beTruthy(void);
NIMBLE_SHORT(id<NMBMatcher> beTruthy(void),
NMB_beTruthy());
NIMBLE_EXPORT id<NMBMatcher> NMB_beFalsy(void);
NIMBLE_SHORT(id<NMBMatcher> beFalsy(void),
NMB_beFalsy());
NIMBLE_EXPORT id<NMBMatcher> NMB_beTrue(void);
NIMBLE_SHORT(id<NMBMatcher> beTrue(void),
NMB_beTrue());
NIMBLE_EXPORT id<NMBMatcher> NMB_beFalse(void);
NIMBLE_SHORT(id<NMBMatcher> beFalse(void),
NMB_beFalse());
NIMBLE_EXPORT id<NMBMatcher> NMB_beNil(void);
NIMBLE_SHORT(id<NMBMatcher> beNil(void),
NMB_beNil());
NIMBLE_EXPORT id<NMBMatcher> NMB_beEmpty(void);
NIMBLE_SHORT(id<NMBMatcher> beEmpty(void),
NMB_beEmpty());
NIMBLE_EXPORT id<NMBMatcher> NMB_containWithNilTermination(id itemOrSubstring, ...) NS_REQUIRES_NIL_TERMINATION;
#define NMB_contain(...) NMB_containWithNilTermination(__VA_ARGS__, nil)
#ifndef NIMBLE_DISABLE_SHORT_SYNTAX
#define contain(...) NMB_contain(__VA_ARGS__)
#endif
NIMBLE_EXPORT id<NMBMatcher> NMB_containElementSatisfying(BOOL(^predicate)(id));
NIMBLE_SHORT(id<NMBMatcher> containElementSatisfying(BOOL(^predicate)(id)),
NMB_containElementSatisfying(predicate));
NIMBLE_EXPORT id<NMBMatcher> NMB_endWith(id itemElementOrSubstring);
NIMBLE_SHORT(id<NMBMatcher> endWith(id itemElementOrSubstring),
NMB_endWith(itemElementOrSubstring));
NIMBLE_EXPORT NMBObjCRaiseExceptionMatcher *NMB_raiseException(void);
NIMBLE_SHORT(NMBObjCRaiseExceptionMatcher *raiseException(void),
NMB_raiseException());
NIMBLE_EXPORT id<NMBMatcher> NMB_match(id expectedValue);
NIMBLE_SHORT(id<NMBMatcher> match(id expectedValue),
NMB_match(expectedValue));
NIMBLE_EXPORT id<NMBMatcher> NMB_allPass(id matcher);
NIMBLE_SHORT(id<NMBMatcher> allPass(id matcher),
NMB_allPass(matcher));
NIMBLE_EXPORT id<NMBMatcher> NMB_satisfyAnyOfWithMatchers(id matchers);
#define NMB_satisfyAnyOf(...) NMB_satisfyAnyOfWithMatchers(@[__VA_ARGS__])
#ifndef NIMBLE_DISABLE_SHORT_SYNTAX
#define satisfyAnyOf(...) NMB_satisfyAnyOf(__VA_ARGS__)
#endif
NIMBLE_EXPORT id<NMBMatcher> NMB_satisfyAllOfWithMatchers(id matchers);
#define NMB_satisfyAllOf(...) NMB_satisfyAllOfWithMatchers(@[__VA_ARGS__])
#ifndef NIMBLE_DISABLE_SHORT_SYNTAX
#define satisfyAllOf(...) NMB_satisfyAllOf(__VA_ARGS__)
#endif
// In order to preserve breakpoint behavior despite using macros to fill in __FILE__ and __LINE__,
// define a builder that populates __FILE__ and __LINE__, and returns a block that takes timeout
// and action arguments. See https://github.com/Quick/Quick/pull/185 for details.
typedef void (^NMBWaitUntilTimeoutBlock)(NSTimeInterval timeout, void (^action)(void (^)(void)));
typedef void (^NMBWaitUntilBlock)(void (^action)(void (^)(void)));
NIMBLE_EXPORT void NMB_failWithMessage(NSString *msg, NSString *file, NSUInteger line);
NIMBLE_EXPORT NMBWaitUntilTimeoutBlock NMB_waitUntilTimeoutBuilder(NSString *file, NSUInteger line);
NIMBLE_EXPORT NMBWaitUntilBlock NMB_waitUntilBuilder(NSString *file, NSUInteger line);
NIMBLE_EXPORT void NMB_failWithMessage(NSString *msg, NSString *file, NSUInteger line);
#define NMB_waitUntilTimeout NMB_waitUntilTimeoutBuilder(@(__FILE__), __LINE__)
#define NMB_waitUntil NMB_waitUntilBuilder(@(__FILE__), __LINE__)
#ifndef NIMBLE_DISABLE_SHORT_SYNTAX
#define expect(...) NMB_expect(^{ return (__VA_ARGS__); }, @(__FILE__), __LINE__)
#define expectAction(BLOCK) NMB_expectAction((BLOCK), @(__FILE__), __LINE__)
#define failWithMessage(msg) NMB_failWithMessage(msg, @(__FILE__), __LINE__)
#define fail() failWithMessage(@"fail() always fails")
#define waitUntilTimeout NMB_waitUntilTimeout
#define waitUntil NMB_waitUntil
#undef NIMBLE_VALUE_OF
#endif
NS_ASSUME_NONNULL_END
-161
View File
@@ -1,161 +0,0 @@
#import <Nimble/DSL.h>
#if __has_include("Nimble-Swift.h")
#import "Nimble-Swift.h"
#else
#import <Nimble/Nimble-Swift.h>
#endif
NS_ASSUME_NONNULL_BEGIN
NIMBLE_EXPORT NIMBLE_OVERLOADABLE NMBExpectation *__nonnull NMB_expect(id __nullable(^actualBlock)(void), NSString *__nonnull file, NSUInteger line) {
return [[NMBExpectation alloc] initWithActualBlock:actualBlock
negative:NO
file:file
line:line];
}
NIMBLE_EXPORT NMBExpectation *NMB_expectAction(void(^actualBlock)(void), NSString *file, NSUInteger line) {
return NMB_expect(^id{
actualBlock();
return nil;
}, file, line);
}
NIMBLE_EXPORT void NMB_failWithMessage(NSString *msg, NSString *file, NSUInteger line) {
return [NMBExpectation failWithMessage:msg file:file line:line];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beAnInstanceOf(Class expectedClass) {
return [NMBObjCMatcher beAnInstanceOfMatcher:expectedClass];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beAKindOf(Class expectedClass) {
return [NMBObjCMatcher beAKindOfMatcher:expectedClass];
}
NIMBLE_EXPORT NIMBLE_OVERLOADABLE NMBObjCBeCloseToMatcher *NMB_beCloseTo(NSNumber *expectedValue) {
return [NMBObjCMatcher beCloseToMatcher:expectedValue within:0.001];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beginWith(id itemElementOrSubstring) {
return [NMBObjCMatcher beginWithMatcher:itemElementOrSubstring];
}
NIMBLE_EXPORT NIMBLE_OVERLOADABLE id<NMBMatcher> NMB_beGreaterThan(NSNumber *expectedValue) {
return [NMBObjCMatcher beGreaterThanMatcher:expectedValue];
}
NIMBLE_EXPORT NIMBLE_OVERLOADABLE id<NMBMatcher> NMB_beGreaterThanOrEqualTo(NSNumber *expectedValue) {
return [NMBObjCMatcher beGreaterThanOrEqualToMatcher:expectedValue];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beIdenticalTo(id expectedInstance) {
return [NMBObjCMatcher beIdenticalToMatcher:expectedInstance];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_be(id expectedInstance) {
return [NMBObjCMatcher beIdenticalToMatcher:expectedInstance];
}
NIMBLE_EXPORT NIMBLE_OVERLOADABLE id<NMBMatcher> NMB_beLessThan(NSNumber *expectedValue) {
return [NMBObjCMatcher beLessThanMatcher:expectedValue];
}
NIMBLE_EXPORT NIMBLE_OVERLOADABLE id<NMBMatcher> NMB_beLessThanOrEqualTo(NSNumber *expectedValue) {
return [NMBObjCMatcher beLessThanOrEqualToMatcher:expectedValue];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beTruthy() {
return [NMBObjCMatcher beTruthyMatcher];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beFalsy() {
return [NMBObjCMatcher beFalsyMatcher];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beTrue() {
return [NMBObjCMatcher beTrueMatcher];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beFalse() {
return [NMBObjCMatcher beFalseMatcher];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beNil() {
return [NMBObjCMatcher beNilMatcher];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_beEmpty() {
return [NMBObjCMatcher beEmptyMatcher];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_containWithNilTermination(id itemOrSubstring, ...) {
NSMutableArray *itemOrSubstringArray = [NSMutableArray array];
if (itemOrSubstring) {
[itemOrSubstringArray addObject:itemOrSubstring];
va_list args;
va_start(args, itemOrSubstring);
id next;
while ((next = va_arg(args, id))) {
[itemOrSubstringArray addObject:next];
}
va_end(args);
}
return [NMBObjCMatcher containMatcher:itemOrSubstringArray];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_containElementSatisfying(BOOL(^predicate)(id)) {
return [NMBObjCMatcher containElementSatisfyingMatcher:predicate];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_endWith(id itemElementOrSubstring) {
return [NMBObjCMatcher endWithMatcher:itemElementOrSubstring];
}
NIMBLE_EXPORT NIMBLE_OVERLOADABLE id<NMBMatcher> NMB_equal(__nullable id expectedValue) {
return [NMBObjCMatcher equalMatcher:expectedValue];
}
NIMBLE_EXPORT NIMBLE_OVERLOADABLE id<NMBMatcher> NMB_haveCount(id expectedValue) {
return [NMBObjCMatcher haveCountMatcher:expectedValue];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_match(id expectedValue) {
return [NMBObjCMatcher matchMatcher:expectedValue];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_allPass(id expectedValue) {
return [NMBObjCMatcher allPassMatcher:expectedValue];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_satisfyAnyOfWithMatchers(id matchers) {
return [NMBObjCMatcher satisfyAnyOfMatcher:matchers];
}
NIMBLE_EXPORT id<NMBMatcher> NMB_satisfyAllOfWithMatchers(id matchers) {
return [NMBObjCMatcher satisfyAllOfMatcher:matchers];
}
NIMBLE_EXPORT NMBObjCRaiseExceptionMatcher *NMB_raiseException() {
return [NMBObjCMatcher raiseExceptionMatcher];
}
NIMBLE_EXPORT NMBWaitUntilTimeoutBlock NMB_waitUntilTimeoutBuilder(NSString *file, NSUInteger line) {
return ^(NSTimeInterval timeout, void (^ _Nonnull action)(void (^ _Nonnull)(void))) {
[NMBWait untilTimeout:timeout file:file line:line action:action];
};
}
NIMBLE_EXPORT NMBWaitUntilBlock NMB_waitUntilBuilder(NSString *file, NSUInteger line) {
return ^(void (^ _Nonnull action)(void (^ _Nonnull)(void))) {
[NMBWait untilFile:file line:line action:action];
};
}
NS_ASSUME_NONNULL_END
@@ -1,11 +0,0 @@
#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
@interface NMBExceptionCapture : NSObject
- (nonnull instancetype)initWithHandler:(void(^ _Nullable)(NSException * _Nonnull))handler finally:(void(^ _Nullable)(void))finally;
- (void)tryBlock:(__attribute__((noescape)) void(^ _Nonnull)(void))unsafeBlock NS_SWIFT_NAME(tryBlock(_:));
@end
typedef void(^NMBSourceCallbackBlock)(BOOL successful);
@@ -1,35 +0,0 @@
#import "NMBExceptionCapture.h"
@interface NMBExceptionCapture ()
@property (nonatomic, copy) void(^ _Nullable handler)(NSException * _Nullable);
@property (nonatomic, copy) void(^ _Nullable finally)(void);
@end
@implementation NMBExceptionCapture
- (nonnull instancetype)initWithHandler:(void(^ _Nullable)(NSException * _Nonnull))handler finally:(void(^ _Nullable)(void))finally {
self = [super init];
if (self) {
self.handler = handler;
self.finally = finally;
}
return self;
}
- (void)tryBlock:(__attribute__((noescape)) void(^ _Nonnull)(void))unsafeBlock {
@try {
unsafeBlock();
}
@catch (NSException *exception) {
if (self.handler) {
self.handler(exception);
}
}
@finally {
if (self.finally) {
self.finally();
}
}
}
@end
@@ -1,18 +0,0 @@
@class NSString;
/**
* Returns a string appropriate for displaying in test output
* from the provided value.
*
* @param anyObject A value that will show up in a test's output.
*
* @return The string that is returned can be
* customized per type by conforming a type to the `TestOutputStringConvertible`
* protocol. When stringifying a non-`TestOutputStringConvertible` type, this
* function will return the value's debug description and then its
* normal description if available and in that order. Otherwise it
* will return the result of constructing a string from the value.
*
* @see `TestOutputStringConvertible`
*/
extern NSString *_Nonnull NMBStringify(id _Nullable anyObject) __attribute__((warn_unused_result));
@@ -1,11 +0,0 @@
#import "NMBStringify.h"
#if __has_include("Nimble-Swift.h")
#import "Nimble-Swift.h"
#else
#import <Nimble/Nimble-Swift.h>
#endif
NSString *_Nonnull NMBStringify(id _Nullable anyObject) {
return [NMBStringer stringify:anyObject];
}
@@ -1,83 +0,0 @@
#import <XCTest/XCTest.h>
#import <objc/runtime.h>
#if __has_include("Nimble-Swift.h")
#import "Nimble-Swift.h"
#else
#import <Nimble/Nimble-Swift.h>
#endif
#pragma mark - Method Swizzling
/// Swaps the implementations between two instance methods.
///
/// @param class The class containing `originalSelector`.
/// @param originalSelector Original method to replace.
/// @param replacementSelector Replacement method.
void swizzleSelectors(Class class, SEL originalSelector, SEL replacementSelector) {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method replacementMethod = class_getInstanceMethod(class, replacementSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(replacementMethod),
method_getTypeEncoding(replacementMethod));
if (didAddMethod) {
class_replaceMethod(class,
replacementSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, replacementMethod);
}
}
#pragma mark - Private
@interface XCTestObservationCenter (Private)
- (void)_addLegacyTestObserver:(id)observer;
@end
@implementation XCTestObservationCenter (Register)
/// Uses objc method swizzling to register `CurrentTestCaseTracker` as a test observer. This is necessary
/// because Xcode 7.3 introduced timing issues where if a custom `XCTestObservation` is registered too early
/// it suppresses all console output (generated by `XCTestLog`), breaking any tools that depend on this output.
/// This approach waits to register our custom test observer until XCTest adds its first "legacy" observer,
/// falling back to registering after the first normal observer if this private method ever changes.
+ (void)load {
if (class_getInstanceMethod([self class], @selector(_addLegacyTestObserver:))) {
// Swizzle -_addLegacyTestObserver:
swizzleSelectors([self class], @selector(_addLegacyTestObserver:), @selector(NMB_original__addLegacyTestObserver:));
} else {
// Swizzle -addTestObserver:, only if -_addLegacyTestObserver: is not implemented
swizzleSelectors([self class], @selector(addTestObserver:), @selector(NMB_original_addTestObserver:));
}
}
#pragma mark - Replacement Methods
/// Registers `CurrentTestCaseTracker` as a test observer after `XCTestLog` has been added.
- (void)NMB_original__addLegacyTestObserver:(id)observer {
[self NMB_original__addLegacyTestObserver:observer];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self addTestObserver:[CurrentTestCaseTracker sharedInstance]];
});
}
/// Registers `CurrentTestCaseTracker` as a test observer after `XCTestLog` has been added.
/// This method is only used if `-_addLegacyTestObserver:` is not impelemented. (added in Xcode 7.3)
- (void)NMB_original_addTestObserver:(id<XCTestObservation>)observer {
[self NMB_original_addTestObserver:observer];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self NMB_original_addTestObserver:[CurrentTestCaseTracker sharedInstance]];
});
}
@end
File diff suppressed because it is too large Load Diff
@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1AB61EB02FDF0033DCB1F8416419F110"
BuildableName = "SwiftAudio.framework"
BlueprintName = "SwiftAudio"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1AB61EB02FDF0033DCB1F8416419F110"
BuildableName = "SwiftAudio.framework"
BlueprintName = "SwiftAudio"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
-201
View File
@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014, Quick Team
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-88
View File
@@ -1,88 +0,0 @@
![](http://f.cl.ly/items/0r1E192C1R0b2g2Q3h2w/QuickLogo_Color.png)
[![Build Status](https://travis-ci.org/Quick/Quick.svg?branch=master)](https://travis-ci.org/Quick/Quick)
[![CocoaPods](https://img.shields.io/cocoapods/v/Quick.svg)](https://cocoapods.org/pods/Quick)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Platforms](https://img.shields.io/cocoapods/p/Quick.svg)](https://cocoapods.org/pods/Quick)
Quick is a behavior-driven development framework for Swift and Objective-C.
Inspired by [RSpec](https://github.com/rspec/rspec), [Specta](https://github.com/specta/specta), and [Ginkgo](https://github.com/onsi/ginkgo).
![](https://raw.githubusercontent.com/Quick/Assets/master/Screenshots/QuickSpec%20screenshot.png)
```swift
// Swift
import Quick
import Nimble
class TableOfContentsSpec: QuickSpec {
override func spec() {
describe("the 'Documentation' directory") {
it("has everything you need to get started") {
let sections = Directory("Documentation").sections
expect(sections).to(contain("Organized Tests with Quick Examples and Example Groups"))
expect(sections).to(contain("Installing Quick"))
}
context("if it doesn't have what you're looking for") {
it("needs to be updated") {
let you = You(awesome: true)
expect{you.submittedAnIssue}.toEventually(beTruthy())
}
}
}
}
}
```
#### Nimble
Quick comes together with [Nimble](https://github.com/Quick/Nimble) — a matcher framework for your tests. You can learn why `XCTAssert()` statements make your expectations unclear and how to fix that using Nimble assertions [here](./Documentation/en-us/NimbleAssertions.md).
## Swift Version
Certain versions of Quick and Nimble only support certain versions of Swift. Depending on which version of Swift your project uses, you should use specific versions of Quick and Nimble. Use the table below to determine which versions of Quick and Nimble are compatible with your project.
|Swift version |Quick version |Nimble version |
|:--------------------|:---------------|:--------------|
|Swift 3 |v1.0.0 or later |v5.0.0 or later|
|Swift 2.2 / Swift 2.3|v0.9.3 |v4.1.0 |
## Documentation
All documentation can be found in the [Documentation folder](./Documentation), including [detailed installation instructions](./Documentation/en-us/InstallingQuick.md) for CocoaPods, Carthage, Git submodules, and more. For example, you can install Quick and [Nimble](https://github.com/Quick/Nimble) using CocoaPods by adding the following to your Podfile:
```rb
# Podfile
use_frameworks!
target "MyApp" do
# Normal libraries
abstract_target 'Tests' do
inherit! :search_paths
target "MyAppTests"
target "MyAppUITests"
pod 'Quick'
pod 'Nimble'
end
end
```
## Projects using Quick
Over ten-thousand apps use either Quick and Nimble however, as they are not included in the app binary, neither appear in “Top Used Libraries” blog posts. Therefore, it would be greatly appreciated to remind contributors that their efforts are valued by compiling a list of organizations and projects that use them.
Does your organization or project use Quick and Nimble? If yes, [please add your project to the list](https://github.com/Quick/Quick/wiki/Projects-using-Quick).
## Who uses Quick
Similar to projects using Quick, it would be nice to hear why people use Quick and Nimble. Are there features you love? Are there features that are just okay? Are there some features we have that no one uses?
Have something positive to say about Quick (or Nimble)? If yes, [provide a testimonial here](https://github.com/Quick/Quick/wiki/Who-uses-Quick).
## License
Apache 2.0 license. See the [`LICENSE`](LICENSE) file for details.
-17
View File
@@ -1,17 +0,0 @@
/**
A `Behavior` encapsulates a set of examples that can be re-used in several locations using the `itBehavesLike` function with a context instance of the generic type.
*/
open class Behavior<Context> {
open static var name: String { return String(describing: self) }
/**
override this method in your behavior to define a set of reusable examples.
This behaves just like an example group defines using `describe` or `context`--it may contain any number of `beforeEach`
and `afterEach` closures, as well as any number of examples (defined using `it`).
- parameter aContext: A closure that, when evaluated, returns a `Context` instance that provide the information on the subject.
*/
open class func spec(_ aContext: @escaping () -> Context) {}
}
-45
View File
@@ -1,45 +0,0 @@
import Foundation
// `#if swift(>=3.2) && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE`
// does not work as expected.
#if swift(>=3.2)
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
@objcMembers
public class _CallsiteBase: NSObject {}
#else
public class _CallsiteBase: NSObject {}
#endif
#else
public class _CallsiteBase: NSObject {}
#endif
/**
An object encapsulating the file and line number at which
a particular example is defined.
*/
final public class Callsite: _CallsiteBase {
/**
The absolute path of the file in which an example is defined.
*/
public let file: String
/**
The line number on which an example is defined.
*/
public let line: UInt
internal init(file: String, line: UInt) {
self.file = file
self.line = line
}
}
extension Callsite {
/**
Returns a boolean indicating whether two Callsite objects are equal.
If two callsites are in the same file and on the same line, they must be equal.
*/
@nonobjc public static func == (lhs: Callsite, rhs: Callsite) -> Bool {
return lhs.file == rhs.file && lhs.line == rhs.line
}
}
@@ -1,161 +0,0 @@
import Foundation
/**
A closure that temporarily exposes a Configuration object within
the scope of the closure.
*/
public typealias QuickConfigurer = (_ configuration: Configuration) -> Void
/**
A closure that, given metadata about an example, returns a boolean value
indicating whether that example should be run.
*/
public typealias ExampleFilter = (_ example: Example) -> Bool
/**
A configuration encapsulates various options you can use
to configure Quick's behavior.
*/
final public class Configuration: NSObject {
internal let exampleHooks = ExampleHooks()
internal let suiteHooks = SuiteHooks()
internal var exclusionFilters: [ExampleFilter] = [ { example in
if let pending = example.filterFlags[Filter.pending] {
return pending
} else {
return false
}
}]
internal var inclusionFilters: [ExampleFilter] = [ { example in
if let focused = example.filterFlags[Filter.focused] {
return focused
} else {
return false
}
}]
/**
Run all examples if none match the configured filters. True by default.
*/
public var runAllWhenEverythingFiltered = true
/**
Registers an inclusion filter.
All examples are filtered using all inclusion filters.
The remaining examples are run. If no examples remain, all examples are run.
- parameter filter: A filter that, given an example, returns a value indicating
whether that example should be included in the examples
that are run.
*/
public func include(_ filter: @escaping ExampleFilter) {
inclusionFilters.append(filter)
}
/**
Registers an exclusion filter.
All examples that remain after being filtered by the inclusion filters are
then filtered via all exclusion filters.
- parameter filter: A filter that, given an example, returns a value indicating
whether that example should be excluded from the examples
that are run.
*/
public func exclude(_ filter: @escaping ExampleFilter) {
exclusionFilters.append(filter)
}
/**
Identical to Quick.Configuration.beforeEach, except the closure is
provided with metadata on the example that the closure is being run
prior to.
*/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
@objc(beforeEachWithMetadata:)
public func beforeEach(_ closure: @escaping BeforeExampleWithMetadataClosure) {
exampleHooks.appendBefore(closure)
}
#else
public func beforeEach(_ closure: @escaping BeforeExampleWithMetadataClosure) {
exampleHooks.appendBefore(closure)
}
#endif
/**
Like Quick.DSL.beforeEach, this configures Quick to execute the
given closure before each example that is run. The closure
passed to this method is executed before each example Quick runs,
globally across the test suite. You may call this method multiple
times across mulitple +[QuickConfigure configure:] methods in order
to define several closures to run before each example.
Note that, since Quick makes no guarantee as to the order in which
+[QuickConfiguration configure:] methods are evaluated, there is no
guarantee as to the order in which beforeEach closures are evaluated
either. Mulitple beforeEach defined on a single configuration, however,
will be executed in the order they're defined.
- parameter closure: The closure to be executed before each example
in the test suite.
*/
public func beforeEach(_ closure: @escaping BeforeExampleClosure) {
exampleHooks.appendBefore(closure)
}
/**
Identical to Quick.Configuration.afterEach, except the closure
is provided with metadata on the example that the closure is being
run after.
*/
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
@objc(afterEachWithMetadata:)
public func afterEach(_ closure: @escaping AfterExampleWithMetadataClosure) {
exampleHooks.appendAfter(closure)
}
#else
public func afterEach(_ closure: @escaping AfterExampleWithMetadataClosure) {
exampleHooks.appendAfter(closure)
}
#endif
/**
Like Quick.DSL.afterEach, this configures Quick to execute the
given closure after each example that is run. The closure
passed to this method is executed after each example Quick runs,
globally across the test suite. You may call this method multiple
times across mulitple +[QuickConfigure configure:] methods in order
to define several closures to run after each example.
Note that, since Quick makes no guarantee as to the order in which
+[QuickConfiguration configure:] methods are evaluated, there is no
guarantee as to the order in which afterEach closures are evaluated
either. Mulitple afterEach defined on a single configuration, however,
will be executed in the order they're defined.
- parameter closure: The closure to be executed before each example
in the test suite.
*/
public func afterEach(_ closure: @escaping AfterExampleClosure) {
exampleHooks.appendAfter(closure)
}
/**
Like Quick.DSL.beforeSuite, this configures Quick to execute
the given closure prior to any and all examples that are run.
The two methods are functionally equivalent.
*/
public func beforeSuite(_ closure: @escaping BeforeSuiteClosure) {
suiteHooks.appendBefore(closure)
}
/**
Like Quick.DSL.afterSuite, this configures Quick to execute
the given closure after all examples have been run.
The two methods are functionally equivalent.
*/
public func afterSuite(_ closure: @escaping AfterSuiteClosure) {
suiteHooks.appendAfter(closure)
}
}
-271
View File
@@ -1,271 +0,0 @@
/**
Defines a closure to be run prior to any examples in the test suite.
You may define an unlimited number of these closures, but there is no
guarantee as to the order in which they're run.
If the test suite crashes before the first example is run, this closure
will not be executed.
- parameter closure: The closure to be run prior to any examples in the test suite.
*/
public func beforeSuite(_ closure: @escaping BeforeSuiteClosure) {
World.sharedWorld.beforeSuite(closure)
}
/**
Defines a closure to be run after all of the examples in the test suite.
You may define an unlimited number of these closures, but there is no
guarantee as to the order in which they're run.
If the test suite crashes before all examples are run, this closure
will not be executed.
- parameter closure: The closure to be run after all of the examples in the test suite.
*/
public func afterSuite(_ closure: @escaping AfterSuiteClosure) {
World.sharedWorld.afterSuite(closure)
}
/**
Defines a group of shared examples. These examples can be re-used in several locations
by using the `itBehavesLike` function.
- parameter name: The name of the shared example group. This must be unique across all shared example
groups defined in a test suite.
- parameter closure: A closure containing the examples. This behaves just like an example group defined
using `describe` or `context`--the closure may contain any number of `beforeEach`
and `afterEach` closures, as well as any number of examples (defined using `it`).
*/
public func sharedExamples(_ name: String, closure: @escaping () -> Void) {
World.sharedWorld.sharedExamples(name) { _ in closure() }
}
/**
Defines a group of shared examples. These examples can be re-used in several locations
by using the `itBehavesLike` function.
- parameter name: The name of the shared example group. This must be unique across all shared example
groups defined in a test suite.
- parameter closure: A closure containing the examples. This behaves just like an example group defined
using `describe` or `context`--the closure may contain any number of `beforeEach`
and `afterEach` closures, as well as any number of examples (defined using `it`).
The closure takes a SharedExampleContext as an argument. This context is a function
that can be executed to retrieve parameters passed in via an `itBehavesLike` function.
*/
public func sharedExamples(_ name: String, closure: @escaping SharedExampleClosure) {
World.sharedWorld.sharedExamples(name, closure: closure)
}
/**
Defines an example group. Example groups are logical groupings of examples.
Example groups can share setup and teardown code.
- parameter description: An arbitrary string describing the example group.
- parameter closure: A closure that can contain other examples.
- parameter flags: A mapping of string keys to booleans that can be used to filter examples or example groups.
*/
public func describe(_ description: String, flags: FilterFlags = [:], closure: () -> Void) {
World.sharedWorld.describe(description, flags: flags, closure: closure)
}
/**
Defines an example group. Equivalent to `describe`.
*/
public func context(_ description: String, flags: FilterFlags = [:], closure: () -> Void) {
World.sharedWorld.context(description, flags: flags, closure: closure)
}
/**
Defines a closure to be run prior to each example in the current example
group. This closure is not run for pending or otherwise disabled examples.
An example group may contain an unlimited number of beforeEach. They'll be
run in the order they're defined, but you shouldn't rely on that behavior.
- parameter closure: The closure to be run prior to each example.
*/
public func beforeEach(_ closure: @escaping BeforeExampleClosure) {
World.sharedWorld.beforeEach(closure)
}
/**
Identical to Quick.DSL.beforeEach, except the closure is provided with
metadata on the example that the closure is being run prior to.
*/
public func beforeEach(_ closure: @escaping BeforeExampleWithMetadataClosure) {
World.sharedWorld.beforeEach(closure: closure)
}
/**
Defines a closure to be run after each example in the current example
group. This closure is not run for pending or otherwise disabled examples.
An example group may contain an unlimited number of afterEach. They'll be
run in the order they're defined, but you shouldn't rely on that behavior.
- parameter closure: The closure to be run after each example.
*/
public func afterEach(_ closure: @escaping AfterExampleClosure) {
World.sharedWorld.afterEach(closure)
}
/**
Identical to Quick.DSL.afterEach, except the closure is provided with
metadata on the example that the closure is being run after.
*/
public func afterEach(_ closure: @escaping AfterExampleWithMetadataClosure) {
World.sharedWorld.afterEach(closure: closure)
}
/**
Defines an example. Examples use assertions to demonstrate how code should
behave. These are like "tests" in XCTest.
- parameter description: An arbitrary string describing what the example is meant to specify.
- parameter closure: A closure that can contain assertions.
- parameter flags: A mapping of string keys to booleans that can be used to filter examples or example groups.
Empty by default.
- parameter file: The absolute path to the file containing the example. A sensible default is provided.
- parameter line: The line containing the example. A sensible default is provided.
*/
public func it(_ description: String, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, closure: @escaping () -> Void) {
World.sharedWorld.it(description, flags: flags, file: file, line: line, closure: closure)
}
/**
Inserts the examples defined using a `sharedExamples` function into the current example group.
The shared examples are executed at this location, as if they were written out manually.
- parameter name: The name of the shared examples group to be executed. This must be identical to the
name of a shared examples group defined using `sharedExamples`. If there are no shared
examples that match the name given, an exception is thrown and the test suite will crash.
- parameter flags: A mapping of string keys to booleans that can be used to filter examples or example groups.
Empty by default.
- parameter file: The absolute path to the file containing the current example group. A sensible default is provided.
- parameter line: The line containing the current example group. A sensible default is provided.
*/
public func itBehavesLike(_ name: String, flags: FilterFlags = [:], file: String = #file, line: UInt = #line) {
itBehavesLike(name, flags: flags, file: file, line: line, sharedExampleContext: { return [:] })
}
/**
Inserts the examples defined using a `sharedExamples` function into the current example group.
The shared examples are executed at this location, as if they were written out manually.
This function also passes those shared examples a context that can be evaluated to give the shared
examples extra information on the subject of the example.
- parameter name: The name of the shared examples group to be executed. This must be identical to the
name of a shared examples group defined using `sharedExamples`. If there are no shared
examples that match the name given, an exception is thrown and the test suite will crash.
- parameter sharedExampleContext: A closure that, when evaluated, returns key-value pairs that provide the
shared examples with extra information on the subject of the example.
- parameter flags: A mapping of string keys to booleans that can be used to filter examples or example groups.
Empty by default.
- parameter file: The absolute path to the file containing the current example group. A sensible default is provided.
- parameter line: The line containing the current example group. A sensible default is provided.
*/
public func itBehavesLike(_ name: String, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, sharedExampleContext: @escaping SharedExampleContext) {
World.sharedWorld.itBehavesLike(name, sharedExampleContext: sharedExampleContext, flags: flags, file: file, line: line)
}
/**
Inserts the examples defined using a `Behavior` into the current example group.
The shared examples are executed at this location, as if they were written out manually.
This function also passes a strongly-typed context that can be evaluated to give the shared examples extra information on the subject of the example.
- parameter behavior: The type of `Behavior` class defining the example group to be executed.
- parameter context: A closure that, when evaluated, returns an instance of `Behavior`'s context type to provide its example group with extra information on the subject of the example.
- parameter flags: A mapping of string keys to booleans that can be used to filter examples or example groups.
Empty by default.
- parameter file: The absolute path to the file containing the current example group. A sensible default is provided.
- parameter line: The line containing the current example group. A sensible default is provided.
*/
public func itBehavesLike<C>(_ behavior: Behavior<C>.Type, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, context: @escaping () -> C) {
World.sharedWorld.itBehavesLike(behavior, context: context, flags: flags, file: file, line: line)
}
/**
Defines an example or example group that should not be executed. Use `pending` to temporarily disable
examples or groups that should not be run yet.
- parameter description: An arbitrary string describing the example or example group.
- parameter closure: A closure that will not be evaluated.
*/
public func pending(_ description: String, closure: () -> Void) {
World.sharedWorld.pending(description, closure: closure)
}
/**
Use this to quickly mark a `describe` closure as pending.
This disables all examples within the closure.
*/
public func xdescribe(_ description: String, flags: FilterFlags, closure: () -> Void) {
World.sharedWorld.xdescribe(description, flags: flags, closure: closure)
}
/**
Use this to quickly mark a `context` closure as pending.
This disables all examples within the closure.
*/
public func xcontext(_ description: String, flags: FilterFlags, closure: () -> Void) {
xdescribe(description, flags: flags, closure: closure)
}
/**
Use this to quickly mark an `it` closure as pending.
This disables the example and ensures the code within the closure is never run.
*/
public func xit(_ description: String, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, closure: @escaping () -> Void) {
World.sharedWorld.xit(description, flags: flags, file: file, line: line, closure: closure)
}
/**
Use this to quicklu mark an `itBehavesLike` closure as pending.
This disables the example group defined by this behavior and ensures the code within is never run.
*/
public func xitBehavesLike<C>(_ behavior: Behavior<C>.Type, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, context: @escaping () -> C) {
World.sharedWorld.xitBehavesLike(behavior, context: context, flags: flags, file: file, line: line)
}
/**
Use this to quickly focus a `describe` closure, focusing the examples in the closure.
If any examples in the test suite are focused, only those examples are executed.
This trumps any explicitly focused or unfocused examples within the closure--they are all treated as focused.
*/
public func fdescribe(_ description: String, flags: FilterFlags = [:], closure: () -> Void) {
World.sharedWorld.fdescribe(description, flags: flags, closure: closure)
}
/**
Use this to quickly focus a `context` closure. Equivalent to `fdescribe`.
*/
public func fcontext(_ description: String, flags: FilterFlags = [:], closure: () -> Void) {
fdescribe(description, flags: flags, closure: closure)
}
/**
Use this to quickly focus an `it` closure, focusing the example.
If any examples in the test suite are focused, only those examples are executed.
*/
public func fit(_ description: String, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, closure: @escaping () -> Void) {
World.sharedWorld.fit(description, flags: flags, file: file, line: line, closure: closure)
}
/**
Use this to quickly focus an `itBehavesLike` closure.
*/
public func fitBehavesLike(_ name: String, flags: FilterFlags = [:], file: String = #file, line: UInt = #line) {
fitBehavesLike(name, flags: flags, file: file, line: line, sharedExampleContext: { return [:] })
}
/**
Use this to quickly focus an `itBehavesLike` closure.
*/
public func fitBehavesLike(_ name: String, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, sharedExampleContext: @escaping SharedExampleContext) {
World.sharedWorld.fitBehavesLike(name, sharedExampleContext: sharedExampleContext, flags: flags, file: file, line: line)
}
/**
Use this to quickly focus on `itBehavesLike` closure.
*/
public func fitBehavesLike<C>(_ behavior: Behavior<C>.Type, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, context: @escaping () -> C) {
World.sharedWorld.fitBehavesLike(behavior, context: context, flags: flags, file: file, line: line)
}
-210
View File
@@ -1,210 +0,0 @@
import Foundation
/**
Adds methods to World to support top-level DSL functions (Swift) and
macros (Objective-C). These functions map directly to the DSL that test
writers use in their specs.
*/
extension World {
internal func beforeSuite(_ closure: @escaping BeforeSuiteClosure) {
suiteHooks.appendBefore(closure)
}
internal func afterSuite(_ closure: @escaping AfterSuiteClosure) {
suiteHooks.appendAfter(closure)
}
internal func sharedExamples(_ name: String, closure: @escaping SharedExampleClosure) {
registerSharedExample(name, closure: closure)
}
internal func describe(_ description: String, flags: FilterFlags, closure: () -> Void) {
guard currentExampleMetadata == nil else {
raiseError("'describe' cannot be used inside '\(currentPhase)', 'describe' may only be used inside 'context' or 'describe'. ")
}
guard currentExampleGroup != nil else {
raiseError("Error: example group was not created by its parent QuickSpec spec. Check that describe() or context() was used in QuickSpec.spec() and not a more general context (i.e. an XCTestCase test)")
}
let group = ExampleGroup(description: description, flags: flags)
currentExampleGroup.appendExampleGroup(group)
performWithCurrentExampleGroup(group, closure: closure)
}
internal func context(_ description: String, flags: FilterFlags, closure: () -> Void) {
guard currentExampleMetadata == nil else {
raiseError("'context' cannot be used inside '\(currentPhase)', 'context' may only be used inside 'context' or 'describe'. ")
}
self.describe(description, flags: flags, closure: closure)
}
internal func fdescribe(_ description: String, flags: FilterFlags, closure: () -> Void) {
var focusedFlags = flags
focusedFlags[Filter.focused] = true
self.describe(description, flags: focusedFlags, closure: closure)
}
internal func xdescribe(_ description: String, flags: FilterFlags, closure: () -> Void) {
var pendingFlags = flags
pendingFlags[Filter.pending] = true
self.describe(description, flags: pendingFlags, closure: closure)
}
internal func beforeEach(_ closure: @escaping BeforeExampleClosure) {
guard currentExampleMetadata == nil else {
raiseError("'beforeEach' cannot be used inside '\(currentPhase)', 'beforeEach' may only be used inside 'context' or 'describe'. ")
}
currentExampleGroup.hooks.appendBefore(closure)
}
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
@objc(beforeEachWithMetadata:)
internal func beforeEach(closure: @escaping BeforeExampleWithMetadataClosure) {
currentExampleGroup.hooks.appendBefore(closure)
}
#else
internal func beforeEach(closure: @escaping BeforeExampleWithMetadataClosure) {
currentExampleGroup.hooks.appendBefore(closure)
}
#endif
internal func afterEach(_ closure: @escaping AfterExampleClosure) {
guard currentExampleMetadata == nil else {
raiseError("'afterEach' cannot be used inside '\(currentPhase)', 'afterEach' may only be used inside 'context' or 'describe'. ")
}
currentExampleGroup.hooks.appendAfter(closure)
}
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
@objc(afterEachWithMetadata:)
internal func afterEach(closure: @escaping AfterExampleWithMetadataClosure) {
currentExampleGroup.hooks.appendAfter(closure)
}
#else
internal func afterEach(closure: @escaping AfterExampleWithMetadataClosure) {
currentExampleGroup.hooks.appendAfter(closure)
}
#endif
@nonobjc
internal func it(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
if beforesCurrentlyExecuting {
raiseError("'it' cannot be used inside 'beforeEach', 'it' may only be used inside 'context' or 'describe'. ")
}
if aftersCurrentlyExecuting {
raiseError("'it' cannot be used inside 'afterEach', 'it' may only be used inside 'context' or 'describe'. ")
}
guard currentExampleMetadata == nil else {
raiseError("'it' cannot be used inside 'it', 'it' may only be used inside 'context' or 'describe'. ")
}
let callsite = Callsite(file: file, line: line)
let example = Example(description: description, callsite: callsite, flags: flags, closure: closure)
currentExampleGroup.appendExample(example)
}
@nonobjc
internal func fit(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
var focusedFlags = flags
focusedFlags[Filter.focused] = true
self.it(description, flags: focusedFlags, file: file, line: line, closure: closure)
}
@nonobjc
internal func xit(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
var pendingFlags = flags
pendingFlags[Filter.pending] = true
self.it(description, flags: pendingFlags, file: file, line: line, closure: closure)
}
@nonobjc
internal func itBehavesLike(_ name: String, sharedExampleContext: @escaping SharedExampleContext, flags: FilterFlags, file: String, line: UInt) {
guard currentExampleMetadata == nil else {
raiseError("'itBehavesLike' cannot be used inside '\(currentPhase)', 'itBehavesLike' may only be used inside 'context' or 'describe'. ")
}
let callsite = Callsite(file: file, line: line)
let closure = World.sharedWorld.sharedExample(name)
let group = ExampleGroup(description: name, flags: flags)
currentExampleGroup.appendExampleGroup(group)
performWithCurrentExampleGroup(group) {
closure(sharedExampleContext)
}
group.walkDownExamples { (example: Example) in
example.isSharedExample = true
example.callsite = callsite
}
}
@nonobjc
internal func fitBehavesLike(_ name: String, sharedExampleContext: @escaping SharedExampleContext, flags: FilterFlags, file: String, line: UInt) {
var focusedFlags = flags
focusedFlags[Filter.focused] = true
self.itBehavesLike(name, sharedExampleContext: sharedExampleContext, flags: focusedFlags, file: file, line: line)
}
internal func itBehavesLike<C>(_ behavior: Behavior<C>.Type, context: @escaping () -> C, flags: FilterFlags, file: String, line: UInt) {
guard currentExampleMetadata == nil else {
raiseError("'itBehavesLike' cannot be used inside '\(currentPhase)', 'itBehavesLike' may only be used inside 'context' or 'describe'. ")
}
let callsite = Callsite(file: file, line: line)
let closure = behavior.spec
let group = ExampleGroup(description: behavior.name, flags: flags)
currentExampleGroup.appendExampleGroup(group)
performWithCurrentExampleGroup(group) {
closure(context)
}
group.walkDownExamples { (example: Example) in
example.isSharedExample = true
example.callsite = callsite
}
}
internal func fitBehavesLike<C>(_ behavior: Behavior<C>.Type, context: @escaping () -> C, flags: FilterFlags, file: String, line: UInt) {
var focusedFlags = flags
focusedFlags[Filter.focused] = true
self.itBehavesLike(behavior, context: context, flags: focusedFlags, file: file, line: line)
}
internal func xitBehavesLike<C>(_ behavior: Behavior<C>.Type, context: @escaping () -> C, flags: FilterFlags, file: String, line: UInt) {
var pendingFlags = flags
pendingFlags[Filter.pending] = true
self.itBehavesLike(behavior, context: context, flags: pendingFlags, file: file, line: line)
}
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
@objc(itWithDescription:flags:file:line:closure:)
internal func objc_it(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
it(description, flags: flags, file: file, line: line, closure: closure)
}
@objc(fitWithDescription:flags:file:line:closure:)
internal func objc_fit(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
fit(description, flags: flags, file: file, line: line, closure: closure)
}
@objc(xitWithDescription:flags:file:line:closure:)
internal func objc_xit(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
xit(description, flags: flags, file: file, line: line, closure: closure)
}
@objc(itBehavesLikeSharedExampleNamed:sharedExampleContext:flags:file:line:)
internal func objc_itBehavesLike(_ name: String, sharedExampleContext: @escaping SharedExampleContext, flags: FilterFlags, file: String, line: UInt) {
itBehavesLike(name, sharedExampleContext: sharedExampleContext, flags: flags, file: file, line: line)
}
#endif
internal func pending(_ description: String, closure: () -> Void) {
print("Pending: \(description)")
}
private var currentPhase: String {
if beforesCurrentlyExecuting {
return "beforeEach"
} else if aftersCurrentlyExecuting {
return "afterEach"
}
return "it"
}
}
-10
View File
@@ -1,10 +0,0 @@
import Foundation
internal func raiseError(_ message: String) -> Never {
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
NSException(name: .internalInconsistencyException, reason: message, userInfo: nil).raise()
#endif
// This won't be reached when ObjC is available and the exception above is raisd
fatalError(message)
}
-134
View File
@@ -1,134 +0,0 @@
import Foundation
private var numberOfExamplesRun = 0
private var numberOfIncludedExamples = 0
// `#if swift(>=3.2) && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE`
// does not work as expected.
#if swift(>=3.2)
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
@objcMembers
public class _ExampleBase: NSObject {}
#else
public class _ExampleBase: NSObject {}
#endif
#else
public class _ExampleBase: NSObject {}
#endif
/**
Examples, defined with the `it` function, use assertions to
demonstrate how code should behave. These are like "tests" in XCTest.
*/
final public class Example: _ExampleBase {
/**
A boolean indicating whether the example is a shared example;
i.e.: whether it is an example defined with `itBehavesLike`.
*/
public var isSharedExample = false
/**
The site at which the example is defined.
This must be set correctly in order for Xcode to highlight
the correct line in red when reporting a failure.
*/
public var callsite: Callsite
weak internal var group: ExampleGroup?
private let internalDescription: String
private let closure: () -> Void
private let flags: FilterFlags
internal init(description: String, callsite: Callsite, flags: FilterFlags, closure: @escaping () -> Void) {
self.internalDescription = description
self.closure = closure
self.callsite = callsite
self.flags = flags
}
public override var description: String {
return internalDescription
}
/**
The example name. A name is a concatenation of the name of
the example group the example belongs to, followed by the
description of the example itself.
The example name is used to generate a test method selector
to be displayed in Xcode's test navigator.
*/
public var name: String {
guard let groupName = group?.name else { return description }
return "\(groupName), \(description)"
}
/**
Executes the example closure, as well as all before and after
closures defined in the its surrounding example groups.
*/
public func run() {
let world = World.sharedWorld
if numberOfIncludedExamples == 0 {
numberOfIncludedExamples = world.includedExampleCount
}
if numberOfExamplesRun == 0 {
world.suiteHooks.executeBefores()
}
let exampleMetadata = ExampleMetadata(example: self, exampleIndex: numberOfExamplesRun)
world.currentExampleMetadata = exampleMetadata
defer {
world.currentExampleMetadata = nil
}
world.exampleHooks.executeBefores(exampleMetadata)
group!.phase = .beforesExecuting
for before in group!.befores {
before(exampleMetadata)
}
group!.phase = .beforesFinished
closure()
group!.phase = .aftersExecuting
for after in group!.afters {
after(exampleMetadata)
}
group!.phase = .aftersFinished
world.exampleHooks.executeAfters(exampleMetadata)
numberOfExamplesRun += 1
if !world.isRunningAdditionalSuites && numberOfExamplesRun >= numberOfIncludedExamples {
world.suiteHooks.executeAfters()
}
}
/**
Evaluates the filter flags set on this example and on the example groups
this example belongs to. Flags set on the example are trumped by flags on
the example group it belongs to. Flags on inner example groups are trumped
by flags on outer example groups.
*/
internal var filterFlags: FilterFlags {
var aggregateFlags = flags
for (key, value) in group!.filterFlags {
aggregateFlags[key] = value
}
return aggregateFlags
}
}
extension Example {
/**
Returns a boolean indicating whether two Example objects are equal.
If two examples are defined at the exact same callsite, they must be equal.
*/
@nonobjc public static func == (lhs: Example, rhs: Example) -> Bool {
return lhs.callsite == rhs.callsite
}
}
-99
View File
@@ -1,99 +0,0 @@
import Foundation
/**
Example groups are logical groupings of examples, defined with
the `describe` and `context` functions. Example groups can share
setup and teardown code.
*/
final public class ExampleGroup: NSObject {
weak internal var parent: ExampleGroup?
internal let hooks = ExampleHooks()
internal var phase: HooksPhase = .nothingExecuted
private let internalDescription: String
private let flags: FilterFlags
private let isInternalRootExampleGroup: Bool
private var childGroups = [ExampleGroup]()
private var childExamples = [Example]()
internal init(description: String, flags: FilterFlags, isInternalRootExampleGroup: Bool = false) {
self.internalDescription = description
self.flags = flags
self.isInternalRootExampleGroup = isInternalRootExampleGroup
}
public override var description: String {
return internalDescription
}
/**
Returns a list of examples that belong to this example group,
or to any of its descendant example groups.
*/
public var examples: [Example] {
return childExamples + childGroups.flatMap { $0.examples }
}
internal var name: String? {
guard let parent = parent else {
return isInternalRootExampleGroup ? nil : description
}
guard let name = parent.name else { return description }
return "\(name), \(description)"
}
internal var filterFlags: FilterFlags {
var aggregateFlags = flags
walkUp { group in
for (key, value) in group.flags {
aggregateFlags[key] = value
}
}
return aggregateFlags
}
internal var befores: [BeforeExampleWithMetadataClosure] {
var closures = Array(hooks.befores.reversed())
walkUp { group in
closures.append(contentsOf: Array(group.hooks.befores.reversed()))
}
return Array(closures.reversed())
}
internal var afters: [AfterExampleWithMetadataClosure] {
var closures = hooks.afters
walkUp { group in
closures.append(contentsOf: group.hooks.afters)
}
return closures
}
internal func walkDownExamples(_ callback: (_ example: Example) -> Void) {
for example in childExamples {
callback(example)
}
for group in childGroups {
group.walkDownExamples(callback)
}
}
internal func appendExampleGroup(_ group: ExampleGroup) {
group.parent = self
childGroups.append(group)
}
internal func appendExample(_ example: Example) {
example.group = self
childExamples.append(example)
}
private func walkUp(_ callback: (_ group: ExampleGroup) -> Void) {
var group = self
while let parent = group.parent {
callback(parent)
group = parent
}
}
}
-37
View File
@@ -1,37 +0,0 @@
import Foundation
// `#if swift(>=3.2) && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE`
// does not work as expected.
#if swift(>=3.2)
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
@objcMembers
public class _ExampleMetadataBase: NSObject {}
#else
public class _ExampleMetadataBase: NSObject {}
#endif
#else
public class _ExampleMetadataBase: NSObject {}
#endif
/**
A class that encapsulates information about an example,
including the index at which the example was executed, as
well as the example itself.
*/
final public class ExampleMetadata: _ExampleMetadataBase {
/**
The example for which this metadata was collected.
*/
public let example: Example
/**
The index at which this example was executed in the
test suite.
*/
public let exampleIndex: Int
internal init(example: Example, exampleIndex: Int) {
self.example = example
self.exampleIndex = exampleIndex
}
}
-44
View File
@@ -1,44 +0,0 @@
import Foundation
// `#if swift(>=3.2) && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE`
// does not work as expected.
#if swift(>=3.2)
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
@objcMembers
public class _FilterBase: NSObject {}
#else
public class _FilterBase: NSObject {}
#endif
#else
public class _FilterBase: NSObject {}
#endif
/**
A mapping of string keys to booleans that can be used to
filter examples or example groups. For example, a "focused"
example would have the flags [Focused: true].
*/
public typealias FilterFlags = [String: Bool]
/**
A namespace for filter flag keys, defined primarily to make the
keys available in Objective-C.
*/
final public class Filter: _FilterBase {
/**
Example and example groups with [Focused: true] are included in test runs,
excluding all other examples without this flag. Use this to only run one or
two tests that you're currently focusing on.
*/
public class var focused: String {
return "focused"
}
/**
Example and example groups with [Pending: true] are excluded from test runs.
Use this to temporarily suspend examples that you know do not pass yet.
*/
public class var pending: String {
return "pending"
}
}
-35
View File
@@ -1,35 +0,0 @@
// MARK: Example Hooks
/**
A closure executed before an example is run.
*/
public typealias BeforeExampleClosure = () -> Void
/**
A closure executed before an example is run. The closure is given example metadata,
which contains information about the example that is about to be run.
*/
public typealias BeforeExampleWithMetadataClosure = (_ exampleMetadata: ExampleMetadata) -> Void
/**
A closure executed after an example is run.
*/
public typealias AfterExampleClosure = BeforeExampleClosure
/**
A closure executed after an example is run. The closure is given example metadata,
which contains information about the example that has just finished running.
*/
public typealias AfterExampleWithMetadataClosure = BeforeExampleWithMetadataClosure
// MARK: Suite Hooks
/**
A closure executed before any examples are run.
*/
public typealias BeforeSuiteClosure = () -> Void
/**
A closure executed after all examples have finished running.
*/
public typealias AfterSuiteClosure = BeforeSuiteClosure
@@ -1,42 +0,0 @@
/**
A container for closures to be executed before and after each example.
*/
final internal class ExampleHooks {
internal var befores: [BeforeExampleWithMetadataClosure] = []
internal var afters: [AfterExampleWithMetadataClosure] = []
internal var phase: HooksPhase = .nothingExecuted
internal func appendBefore(_ closure: @escaping BeforeExampleWithMetadataClosure) {
befores.append(closure)
}
internal func appendBefore(_ closure: @escaping BeforeExampleClosure) {
befores.append { (_: ExampleMetadata) in closure() }
}
internal func appendAfter(_ closure: @escaping AfterExampleWithMetadataClosure) {
afters.append(closure)
}
internal func appendAfter(_ closure: @escaping AfterExampleClosure) {
afters.append { (_: ExampleMetadata) in closure() }
}
internal func executeBefores(_ exampleMetadata: ExampleMetadata) {
phase = .beforesExecuting
for before in befores {
before(exampleMetadata)
}
phase = .beforesFinished
}
internal func executeAfters(_ exampleMetadata: ExampleMetadata) {
phase = .aftersExecuting
for after in afters {
after(exampleMetadata)
}
phase = .aftersFinished
}
}
-11
View File
@@ -1,11 +0,0 @@
/**
A description of the execution cycle of the current example with
respect to the hooks of that example.
*/
internal enum HooksPhase {
case nothingExecuted
case beforesExecuting
case beforesFinished
case aftersExecuting
case aftersFinished
}
-32
View File
@@ -1,32 +0,0 @@
/**
A container for closures to be executed before and after all examples.
*/
final internal class SuiteHooks {
internal var befores: [BeforeSuiteClosure] = []
internal var afters: [AfterSuiteClosure] = []
internal var phase: HooksPhase = .nothingExecuted
internal func appendBefore(_ closure: @escaping BeforeSuiteClosure) {
befores.append(closure)
}
internal func appendAfter(_ closure: @escaping AfterSuiteClosure) {
afters.append(closure)
}
internal func executeBefores() {
phase = .beforesExecuting
for before in befores {
before()
}
phase = .beforesFinished
}
internal func executeAfters() {
phase = .aftersExecuting
for after in afters {
after()
}
phase = .aftersFinished
}
}
@@ -1,25 +0,0 @@
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
import Foundation
extension Bundle {
/**
Locates the first bundle with a '.xctest' file extension.
*/
internal static var currentTestBundle: Bundle? {
return allBundles.first { $0.bundlePath.hasSuffix(".xctest") }
}
/**
Return the module name of the bundle.
Uses the bundle filename and transform it to match Xcode's transformation.
Module name has to be a valid "C99 extended identifier".
*/
internal var moduleName: String {
let fileName = bundleURL.fileName as NSString
return fileName.c99ExtendedIdentifier
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More