Compare commits

...

100 Commits

Author SHA1 Message Date
Jørgen Henrichsen a0efa5f408 Update README
Bump Carthage install release version
2018-11-02 19:26:03 +01:00
Jørgen Henrichsen 74cafd4c42 Merge pull request #31 from jorgenhenrichsen/feature/support-carthage
Feature/support carthage
2018-11-02 19:18:23 +01:00
Jørgen Henrichsen 53780ac03e Update Readme
Added info about installing via Carthage.
2018-11-02 18:24:31 +01:00
Jørgen Henrichsen ba438c8ede Set SwiftAudio scheme as shared. 2018-11-02 17:53:37 +01:00
Jørgen Henrichsen 06cb6577c1 Merge pull request #30 from jorgenhenrichsen/dev
Dev into master
2018-11-02 15:33:25 +01:00
Jørgen Henrichsen 5a0f379275 Update podspec. Update README.
- Bumped version to 0.4.0
- Updated the readme iwth new content and clarifications
2018-11-02 15:12:22 +01:00
Jørgen Henrichsen 11e793f963 Fixed typo. Added docs.
- Fixed type in the `timeEventFrequency`-property
- Added doc comment to `automaticallyWaitsToMinimizeStalling`
2018-11-02 15:11:01 +01:00
Jørgen Henrichsen d094067ac7 Use old syntax for AVAudioSession Options and Category 2018-11-02 13:54:07 +01:00
Jørgen Henrichsen 4bacd5f1ff Fixed problem in addItems(). Added more tests.
- QueueManager's addItems() incremented the currentIndex with the managers item count. Corrected to only increment by the added item count.
- Added tests for the QueueManager.
2018-11-02 13:13:50 +01:00
Jørgen Henrichsen 73089bbe8b Use AudioSession protocol instead of AVAudioSession.
- Makes the AudioSessionController easier to test.
- Updated tests
- Fixes a problem where the AudioSessionController tests would not succeed on device.
2018-11-02 08:01:15 +01:00
Jørgen Henrichsen b4f919e8f4 Merge branch 'master' into dev 2018-10-29 20:56:14 +01:00
Jørgen Henrichsen 7e33789644 Update README
Change install instructions to use 0.3.6
2018-10-29 19:10:19 +01:00
Jørgen Henrichsen 843ba9f450 Bumped version to 0.3.6 2018-10-29 18:53:26 +01:00
Jørgen Henrichsen 4305110867 Merge pull request #28 from dcvz/master
Add a Couple of Features and Fixes
2018-10-29 18:50:48 +01:00
David Chavez cd43ecc6f9 Update example and fix tests 2018-10-29 15:44:06 +01:00
David Chavez 274377af3f Address review comments 2018-10-29 14:21:09 +01:00
Jørgen Henrichsen ef41885eaf Merge pull request #29 from jorgenhenrichsen/SwiftAudio-1
Restructuring and simplifying
2018-10-28 22:54:06 +01:00
Jørgen Henrichsen 21da2da43b Update README
Added part about AudioPlayerDelegate and adding additional NowPlayingInfo
2018-10-28 22:35:02 +01:00
Jørgen Henrichsen 336d5586bf Added a couple of tests to the AVPlayerWrapper 2018-10-28 22:29:29 +01:00
Jørgen Henrichsen e917867220 Updated tests 2018-10-28 19:24:20 +01:00
Jørgen Henrichsen 7897a79a79 Added options for volume, muting and automaticallyWaitingToMinimizeStalling 2018-10-28 19:20:14 +01:00
David Chavez 43824a9700 Add granularity for a track finishing playback between simple/queued 2018-10-28 13:37:02 +01:00
Jørgen Henrichsen 460af7ab1e Added tests for the currentItem in the AudioPlayer 2018-10-28 12:44:52 +01:00
Jørgen Henrichsen 670bf0e09e Made enable(disable commands internal 2018-10-28 12:42:21 +01:00
Jørgen Henrichsen 238c02db12 Removed unneccessary comments.
Made the enable commands method private, as there is no need for it to be public.
2018-10-28 12:38:43 +01:00
Jørgen Henrichsen 20190152eb Remove the add(propert:) for adding new NowPlayingInfo properties.
Made the nowPlayingInfoController public.
2018-10-28 12:30:47 +01:00
David Chavez 9e6683674b Fix issues with updating index upon queue mutations 2018-10-28 12:02:45 +01:00
David Chavez b27332aafb Add a couple of features and fix some issues 2018-10-28 10:35:44 +01:00
Jørgen Henrichsen 18688ab766 QueuedAudioPlayer load(item:) will replace the current item in the queue. 2018-10-26 14:07:47 +02:00
Jørgen Henrichsen 6835738754 Update README 2018-10-26 13:23:07 +02:00
Jørgen Henrichsen 9f05915993 Removed testing code snippet breaking the AVPlayerWrapper 2018-10-26 13:12:38 +02:00
Jørgen Henrichsen 2f9a5481ff Removed warnings 2018-10-26 13:09:49 +02:00
Jørgen Henrichsen 079f8c8ce1 Update travis.yml
Change back to Xcode 9
2018-10-26 12:57:38 +02:00
Jørgen Henrichsen 720b4739b4 Update travis.yml
Roll back to iOS 11.4 simulator for testing, as there is an issue with loading duration on the player with iOS 12 simulators #26
2018-10-26 12:52:42 +02:00
Jørgen Henrichsen 2b150b5652 Merge branch 'SwiftAudio-1' of github.com:jorgenhenrichsen/SwiftAudio into SwiftAudio-1 2018-10-26 12:25:14 +02:00
Jørgen Henrichsen 4adc84aaf3 Moved files to correct folder. 2018-10-26 12:24:48 +02:00
Jørgen Henrichsen 76f2d22e7a Update travis.yml
Set SDK version to iOS 12.0
2018-10-26 12:18:40 +02:00
Jørgen Henrichsen 3c409200ee Update travis.yml
Change osx_image to Xcode 10.
Change iOS version to iOS 12.
2018-10-26 12:13:36 +02:00
Jørgen Henrichsen b44777fcd7 Changed method signature for loading items. 2018-10-26 11:28:55 +02:00
Jørgen Henrichsen d682fd9468 Removed outdated comment. 2018-10-26 11:22:30 +02:00
Jørgen Henrichsen 10e6c46c18 Updated AudioPlayer tests 2018-10-26 11:18:26 +02:00
Jørgen Henrichsen 89715d9d38 Made it possible to supply custom AVPlayer, NowPlayingInfoController and RemoteCommandController in the AudioPlayer init(). 2018-10-26 11:11:58 +02:00
Jørgen Henrichsen 99bd43769c Folder structure in the project 2018-10-26 11:00:15 +02:00
Jørgen Henrichsen ce5e5e886f Removed SimpleAudioPlayer, will use AudioPlayer from now on.
Removed the tests.
2018-10-26 10:57:31 +02:00
Jørgen Henrichsen 521141ba0d Moved the AVPlayerWrapperDelegate to a seperate file. 2018-10-26 10:54:08 +02:00
Jørgen Henrichsen d3d354d5bd Simplified AVPlayerWrapper.
Created a protocol for the wrapper to follow.
Removed config options that where just getters/setters to the underlying AVPlayer.
Made it possible to pass in custom AVPlayer that can be customized with these options.
Updated AVPlayerWrapperTests and added a couple tests for the rate property.
2018-10-26 10:50:55 +02:00
Jørgen Henrichsen 664c56b79c Update README
Add version to pod file instruction
2018-10-25 10:49:53 +02:00
Jørgen Henrichsen 7bb83e87e0 Merge pull request #25 from jorgenhenrichsen/dev
Update master
2018-10-23 10:12:46 +02:00
Jørgen Henrichsen 6cad96b4f8 Update podscpec
Bump version to 0.3.5
2018-10-22 14:20:40 +02:00
Jørgen Henrichsen 50a58c2306 Merge pull request #24 from jorgenhenrichsen/handle-remote-commands
Handle remote commands
2018-10-22 14:18:17 +02:00
Jørgen Henrichsen 2dbaf3d4dc Update Readme
Add description of how to override remote command handlers.
2018-10-22 10:11:04 +02:00
Jørgen Henrichsen 851d8704d9 Made remotecommandcontroller public. 2018-10-21 19:44:24 +02:00
Jørgen Henrichsen 7c4dc27868 Made it possible to override remote command handlers 2018-10-21 10:42:29 +02:00
jorgenhenrichsen 0df6386786 Merge pull request #23 from jorgenhenrichsen/dev
Dev
2018-10-10 12:36:46 +02:00
jorgenhenrichsen b58c40e7c3 Merge branch 'master' into dev 2018-10-10 12:16:22 +02:00
Jørgen Henrichsen 77a3206633 Update podspec
Bmump version to 0.3.4
2018-10-10 12:05:55 +02:00
jorgenhenrichsen d7c8c0fc0d Merge pull request #20 from jorgenhenrichsen/fix/play-pause-throwing
Only throw noLoadedItemError when no item is loaded.
2018-10-10 10:03:45 +02:00
Jørgen Henrichsen e8354dfdfb Test play()/pause() with no item loaded 2018-10-10 08:55:53 +02:00
Jørgen Henrichsen 2e9c8733fc Use guard to check for error conditions 2018-10-10 08:55:37 +02:00
jorgenhenrichsen 97353b98d9 Merge branch 'dev' into fix/play-pause-throwing 2018-10-10 07:10:24 +02:00
jorgenhenrichsen 0e3e8ec5b9 Merge pull request #22 from jorgenhenrichsen/update-pods
Updated pods
2018-10-10 07:10:03 +02:00
Jørgen Henrichsen f8dbe9d312 Update travis config
Update pod repo on pod install
2018-10-10 06:29:20 +02:00
Jørgen Henrichsen 9f4ef3aa9e Updated pods 2018-10-10 06:08:05 +02:00
Jørgen Henrichsen 4097b66aac Only throw noLoadedItemError when no item is loaded.
This error was thrown when calling pause() when already paused and play() when already playing.
2018-10-09 16:45:33 +02:00
jorgenhenrichsen 454e036449 Merge pull request #19 from jorgenhenrichsen/dev
Dev
2018-09-27 15:49:55 +02:00
Jørgen Henrichsen 8fb3b8424d Update podspec
Bumped to version 0.3.3
2018-09-27 15:39:40 +02:00
jorgenhenrichsen 731863a900 Merge pull request #18 from jorgenhenrichsen/interruptions
Interruptions
2018-09-27 15:34:03 +02:00
jorgenhenrichsen 45582b3f43 Update README
More info about interruptions
2018-09-27 15:25:15 +02:00
Jørgen Henrichsen 534b62da9e Added tests for interruption notifications 2018-09-27 14:50:33 +02:00
Jørgen Henrichsen e3cb3c4a51 Added possiblity to turn of interruption notifications 2018-09-27 14:50:10 +02:00
Jørgen Henrichsen a0e05a885a Update README
Added part about interruptions.
2018-09-27 09:53:09 +02:00
Jørgen Henrichsen 1a13887a65 Added interruption handler and delegate protocol. 2018-09-27 09:52:52 +02:00
jorgenhenrichsen da6e83f64a Merge pull request #17 from jorgenhenrichsen/testing
Testing
2018-09-25 20:10:14 +02:00
Jørgen Henrichsen 0f2bfb3e79 Use long source when testing SimpleAudioPlayer 2018-09-25 19:52:38 +02:00
Jørgen Henrichsen eabeca93a0 Use shorter buffer duration for audio tests. 2018-09-21 17:35:20 +02:00
Jørgen Henrichsen 6a93840463 More tests
Added for SimpleAudioPlayer and QueuedAudioPlayer.
Made next and previous items in the queue non-optional, instead they will be empty when no items are in the queue.
2018-09-06 23:39:03 +02:00
Jørgen Henrichsen 1261f88803 Removed play to end tests. 2018-08-18 09:29:44 +02:00
Jørgen Henrichsen 6239d6d5e6 Shorter test sound 2018-08-18 09:23:19 +02:00
Jørgen Henrichsen a63771a040 More tests for AVPlayerWrapper and AudioPlayer 2018-08-18 09:03:06 +02:00
Jørgen Henrichsen 7d27ef286f Add the TestSound to the project 2018-08-05 15:28:27 +02:00
Jørgen Henrichsen bf0d3a79dd Use a smaller, silent test sound. 2018-08-05 14:59:31 +02:00
Jørgen Henrichsen 22dec9d6b1 Use low buffer duration when testing audio 2018-08-05 13:48:39 +02:00
jorgenhenrichsen b7be6338c6 Add codecov badge to README 2018-08-05 13:36:38 +02:00
jorgenhenrichsen 97b70d056a Added codecov yml
Ignore Example directory
2018-08-05 13:25:57 +02:00
jorgenhenrichsen c9bce64bc1 Only upload coverage for SwiftAudio 2018-08-05 13:14:57 +02:00
Jørgen Henrichsen dafcbdfe3e Set automaticallyWaitsToMinimizeStalling to false in test case 2018-08-05 13:04:46 +02:00
jorgenhenrichsen 24666c3ab5 Merge pull request #15 from jorgenhenrichsen/master
Master -> Dev
2018-08-05 12:57:18 +02:00
jorgenhenrichsen 8de5d678b8 Use iOS 11.4 on simulator 2018-08-05 12:51:39 +02:00
jorgenhenrichsen 70a55f22e0 Update travis.yml
*Use Xcode 9.4
*Use Swift lang
2018-08-05 12:47:03 +02:00
jorgenhenrichsen e180fc7a72 Add codecov coverage uploading 2018-08-05 12:38:39 +02:00
Jørgen Henrichsen 9008c6f9a0 Renamed the delegate holder 2018-08-05 12:23:44 +02:00
Jørgen Henrichsen d49c522032 Added test case in AudioPlayerSessionControllerTests
Added case for deactivating the audio session.
2018-08-05 12:23:10 +02:00
Jørgen Henrichsen 7681d5d983 Added AudioPlayerTests 2018-08-05 12:22:39 +02:00
Jørgen Henrichsen 8b1e57b3c0 Added a Source file for test audio.
Contains the test audio file path, used in all tests.
2018-08-05 10:32:30 +02:00
Jørgen Henrichsen 1c4f5ec738 AudioSessionController tests.
Also activate audio session on play instead of launch in the example.
2018-08-05 10:02:53 +02:00
Jørgen Henrichsen d2ed064295 Test seeking function of AVPlayerWrapper 2018-08-05 09:07:34 +02:00
jorgenhenrichsen 0d4060eb68 Merge pull request #14 from jorgenhenrichsen/remote-commands
Remote commands
2018-08-04 19:59:41 +02:00
Jørgen Henrichsen 15a8bc4abd Bumped pod version 2018-08-04 19:53:59 +02:00
Jørgen Henrichsen da5b7702f7 Use next/prev commands in the example. 2018-08-04 08:55:44 +02:00
Jørgen Henrichsen 0066a4121c Added next and previous remote commands.
Also improved error handling in the RemoteCommandController.
2018-08-04 08:55:04 +02:00
122 changed files with 3566 additions and 2305 deletions
+2
View File
@@ -0,0 +1,2 @@
ignore:
- "Example/.*"
+7 -4
View File
@@ -2,13 +2,16 @@
# * http://www.objc.io/issue-6/travis-ci.html
# * https://github.com/supermarin/xcpretty#usage
osx_image: xcode9.2
language: objective-c
osx_image: xcode9.4
language: swift
cache: cocoapods
podfile: Example/Podfile
before_install:
- gem install cocoapods # Since Travis is not always on latest version
- pod install --project-directory=Example
- pod install --project-directory=Example --repo-update
script:
- set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/SwiftAudio.xcworkspace -scheme SwiftAudio-Example -sdk iphonesimulator11.2 -destination "OS=11.2,name=iPhone X" | xcpretty
- set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/SwiftAudio.xcworkspace -scheme SwiftAudio-Example -sdk iphonesimulator11.4 -destination "OS=11.4,name=iPhone X" | xcpretty
- pod lib lint
after_success:
- bash <(curl -s https://codecov.io/bash) -J 'SwiftAudio'
+2 -2
View File
@@ -7,8 +7,8 @@ target 'SwiftAudio_Example' do
target 'SwiftAudio_Tests' do
inherit! :search_paths
pod 'Quick'
pod 'Nimble'
pod 'Quick', '~> 1.3.0'
pod 'Nimble' , '~> 7.3.0'
end
end
+16 -11
View File
@@ -1,22 +1,27 @@
PODS:
- Nimble (7.0.3)
- Quick (1.2.0)
- SwiftAudio (0.1.0)
- Nimble (7.3.1)
- Quick (1.3.2)
- SwiftAudio (0.3.3)
DEPENDENCIES:
- Nimble
- Quick
- Nimble (~> 7.3.0)
- Quick (~> 1.3.0)
- SwiftAudio (from `../`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- Nimble
- Quick
EXTERNAL SOURCES:
SwiftAudio:
:path: ../
:path: "../"
SPEC CHECKSUMS:
Nimble: 7f5a9c447a33002645a071bddafbfb24ea70e0ac
Quick: 58d203b1c5e27fff7229c4c1ae445ad7069a7a08
SwiftAudio: a3a2d2c800a7d47687dcfd7c35ab757462b75856
Nimble: 04f732da099ea4d153122aec8c2a88fd0c7219ae
Quick: 2623cb30d7a7f41ca62f684f679586558f483d46
SwiftAudio: 2e712c3e04cf172d05639d7bb1516db7afd195da
PODFILE CHECKSUM: 4725c63cba8dedecdf397c1768967bd269bf4532
PODFILE CHECKSUM: 8a75946cbc65d8d98176f80a88d8363a28d118ce
COCOAPODS: 1.3.1
COCOAPODS: 1.5.3
+6 -6
View File
@@ -1,9 +1,9 @@
{
"name": "SwiftAudio",
"version": "0.1.0",
"summary": "A short description of SwiftAudio.",
"description": "TODO: Add long description of the pod here.",
"homepage": "https://github.com/rgen Henrichsen/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"
@@ -12,8 +12,8 @@
"Jørgen Henrichsen": "jh.henrichs@gmail.com"
},
"source": {
"git": "https://github.com/rgen Henrichsen/SwiftAudio.git",
"tag": "0.1.0"
"git": "https://github.com/jorgenhenrichsen/SwiftAudio.git",
"tag": "0.3.3"
},
"platforms": {
"ios": "10.0"
+16 -11
View File
@@ -1,22 +1,27 @@
PODS:
- Nimble (7.0.3)
- Quick (1.2.0)
- SwiftAudio (0.1.0)
- Nimble (7.3.1)
- Quick (1.3.2)
- SwiftAudio (0.3.3)
DEPENDENCIES:
- Nimble
- Quick
- Nimble (~> 7.3.0)
- Quick (~> 1.3.0)
- SwiftAudio (from `../`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- Nimble
- Quick
EXTERNAL SOURCES:
SwiftAudio:
:path: ../
:path: "../"
SPEC CHECKSUMS:
Nimble: 7f5a9c447a33002645a071bddafbfb24ea70e0ac
Quick: 58d203b1c5e27fff7229c4c1ae445ad7069a7a08
SwiftAudio: a3a2d2c800a7d47687dcfd7c35ab757462b75856
Nimble: 04f732da099ea4d153122aec8c2a88fd0c7219ae
Quick: 2623cb30d7a7f41ca62f684f679586558f483d46
SwiftAudio: 2e712c3e04cf172d05639d7bb1516db7afd195da
PODFILE CHECKSUM: 4725c63cba8dedecdf397c1768967bd269bf4532
PODFILE CHECKSUM: 8a75946cbc65d8d98176f80a88d8363a28d118ce
COCOAPODS: 1.3.1
COCOAPODS: 1.5.3
@@ -25,13 +25,11 @@ import CwlCatchExceptionSupport
#endif
private func catchReturnTypeConverter<T: NSException>(_ type: T.Type, block: () -> Void) -> T? {
// Get the type from an *instance*, instead of a receiving the type directly
return catchExceptionOfKind(type, block) as? T
}
extension NSException {
public static func catchException(in block: () -> Void) -> Self? {
// Use a dummy instance of Self to provide the type
return catchReturnTypeConverter(self, block: block)
}
}
@@ -97,8 +97,9 @@ import Foundation
// 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, request.Head.msgh_size, context.currentExceptionPort, 0, UInt32(MACH_PORT_NULL))
mach_msg(requestPtr, MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0, requestSize, context.currentExceptionPort, 0, UInt32(MACH_PORT_NULL))
} }
// Prepare the reply structure
@@ -121,8 +122,9 @@ import Foundation
}
// Send the reply
let replySize = reply.Head.msgh_size
try kernCheck { reply.withMsgHeaderPointer { replyPtr in
mach_msg(replyPtr, MACH_SEND_MSG, reply.Head.msgh_size, 0, UInt32(MACH_PORT_NULL), 0, UInt32(MACH_PORT_NULL))
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.
@@ -20,6 +20,8 @@
#import <Foundation/Foundation.h>
extern bool _swift_reportFatalErrorsToDebugger;
//! Project version number for CwlUtils.
FOUNDATION_EXPORT double CwlPreconditionTestingVersionNumber;
+42 -42
View File
@@ -226,9 +226,9 @@ exception once evaluated:
// that Nimble will catch.
// (see https://github.com/Quick/Nimble/issues/220#issuecomment-172667064)
let exception = NSException(
name: NSInternalInconsistencyException,
reason: "Not enough fish in the sea.",
userInfo: ["something": "is fishy"])
name: NSInternalInconsistencyException,
reason: "Not enough fish in the sea.",
userInfo: ["something": "is fishy"])
expect { exception.raise() }.to(raiseException())
// Also, you can customize raiseException to be more specific
@@ -714,7 +714,7 @@ expect(actual) ≈ expected
expect(actual) ≈ (expected, delta)
```
(Type Option-x to get on a U.S. keyboard)
(Type <kbd>option</kbd>+<kbd>x</kbd> to get `` on a U.S. keyboard)
The former version uses the default delta of 0.0001. Here is yet another way to do this:
@@ -725,7 +725,7 @@ expect(actual) ≈ expected ± delta
expect(actual) == expected ± delta
```
(Type Option-Shift-= to get ± on a U.S. keyboard)
(Type <kbd>option</kbd>+<kbd>shift</kbd>+<kbd>=</kbd> to get `±` on a U.S. keyboard)
If you are comparing arrays of floating point numbers, you'll find the following useful:
@@ -1043,10 +1043,10 @@ let turtles: [Turtle] = functionThatReturnsSomeTurtlesInAnyOrder()
// [{color: "blue"}, {color: "green"}] or [{color: "green"}, {color: "blue"}]:
expect(turtles).to(containElementSatisfying({ turtle in
return turtle.color == "green"
return turtle.color == "green"
}))
expect(turtles).to(containElementSatisfying({ turtle in
return turtle.color == "blue"
return turtle.color == "blue"
}, "that is a turtle with color 'blue'"))
// The second matcher will incorporate the provided string in the error message
@@ -1069,10 +1069,10 @@ NSArray<Turtle *> * __nonnull turtles = functionThatReturnsSomeTurtlesInAnyOrder
// [{color: "blue"}, {color: "green"}] or [{color: "green"}, {color: "blue"}]:
expect(turtles).to(containElementSatisfying(^BOOL(id __nonnull object) {
return [[turtle color] isEqualToString:@"green"];
return [[turtle color] isEqualToString:@"green"];
}));
expect(turtles).to(containElementSatisfying(^BOOL(id __nonnull object) {
return [[turtle color] isEqualToString:@"blue"];
return [[turtle color] isEqualToString:@"blue"];
}));
```
@@ -1273,24 +1273,24 @@ value and return a `Predicate` closure. Take `equal`, for example:
// Swift
public func equal<T: Equatable>(expectedValue: T?) -> Predicate<T> {
// Can be shortened to:
// Predicate { actual in ... }
//
// But shown with types here for clarity.
return Predicate { (actual: Expression<T>) throws -> PredicateResult in
let msg = ExpectationMessage.expectedActualValueTo("equal <\(expectedValue)>")
if let actualValue = try actualExpression.evaluate() {
return PredicateResult(
bool: actualValue == expectedValue!,
message: msg
)
} else {
return PredicateResult(
status: .fail,
message: msg.appendedBeNilHint()
)
// Can be shortened to:
// Predicate { actual in ... }
//
// But shown with types here for clarity.
return Predicate { (actual: Expression<T>) throws -> PredicateResult in
let msg = ExpectationMessage.expectedActualValueTo("equal <\(expectedValue)>")
if let actualValue = try actualExpression.evaluate() {
return PredicateResult(
bool: actualValue == expectedValue!,
message: msg
)
} else {
return PredicateResult(
status: .fail,
message: msg.appendedBeNilHint()
)
}
}
}
}
```
@@ -1382,11 +1382,11 @@ custom matchers should call `actualExpression.evaluate()`:
// Swift
public func beNil<T>() -> Predicate<T> {
// Predicate.simpleNilable(..) automatically generates ExpectationMessage for
// us based on the string we provide to it. Also, the 'Nilable' postfix indicates
// that this Predicate supports matching against nil actualExpressions, instead of
// always resulting in a PredicateStatus.fail result -- which is true for
// Predicate.simple(..)
// Predicate.simpleNilable(..) automatically generates ExpectationMessage for
// us based on the string we provide to it. Also, the 'Nilable' postfix indicates
// that this Predicate supports matching against nil actualExpressions, instead of
// always resulting in a PredicateStatus.fail result -- which is true for
// Predicate.simple(..)
return Predicate.simpleNilable("be nil") { actualExpression in
let actualValue = try actualExpression.evaluate()
return PredicateStatus(bool: actualValue == nil)
@@ -1412,9 +1412,9 @@ against the one provided to the matcher function, and passes if they are the sam
// Swift
public func haveDescription(description: String) -> Predicate<Printable?> {
return Predicate.simple("have description") { actual in
return PredicateStatus(bool: actual.evaluate().description == description)
}
return Predicate.simple("have description") { actual in
return PredicateStatus(bool: actual.evaluate().description == description)
}
}
```
@@ -1489,7 +1489,7 @@ case expectedCustomValueTo(/* message: */ String, /* actual: */ String)
// Emits standard error message without mentioning the actual value
// eg - "expected to <message>"
case expectedTo(/* message: */ String, /* actual: */ String)
case expectedTo(/* message: */ String)
// ...
}
@@ -1526,13 +1526,13 @@ custom matcher. The example below defines the class method
// Swift
extension NMBObjCMatcher {
public class func beNilMatcher() -> NMBObjCMatcher {
return NMBObjCMatcher { actualBlock, failureMessage, location in
let block = ({ actualBlock() as NSObject? })
let expr = Expression(expression: block, location: location)
return beNil().matches(expr, failureMessage: failureMessage)
public class func beNilMatcher() -> NMBObjCMatcher {
return NMBObjCMatcher { actualBlock, failureMessage, location in
let block = ({ actualBlock() as NSObject? })
let expr = Expression(expression: block, location: location)
return beNil().matches(expr, failureMessage: failureMessage)
}
}
}
}
```
@@ -1551,7 +1551,7 @@ class method:
// Objective-C
FOUNDATION_EXPORT id<NMBMatcher> beNil() {
return [NMBObjCMatcher beNilMatcher];
return [NMBObjCMatcher beNilMatcher];
}
```
@@ -65,7 +65,7 @@ public func withAssertionHandler(_ tempAssertionHandler: AssertionHandler, closu
/// assertion handler when this is true. Defaults to false.
///
/// @see gatherFailingExpectations
public func gatherExpectations(silently: Bool = false, closure: @escaping () -> Void) -> [AssertionRecord] {
public func gatherExpectations(silently: Bool = false, closure: () -> Void) -> [AssertionRecord] {
let previousRecorder = NimbleEnvironment.activeInstance.assertionHandler
let recorder = AssertionRecorder()
let handlers: [AssertionHandler]
@@ -92,7 +92,7 @@ public func gatherExpectations(silently: Bool = false, closure: @escaping () ->
///
/// @see gatherExpectations
/// @see raiseException source for an example use case.
public func gatherFailingExpectations(silently: Bool = false, closure: @escaping () -> Void) -> [AssertionRecord] {
public func gatherFailingExpectations(silently: Bool = false, closure: () -> Void) -> [AssertionRecord] {
let assertions = gatherExpectations(silently: silently, closure: closure)
return assertions.filter { assertion in
!assertion.success
@@ -4,7 +4,7 @@ import Foundation
private func from(objcPredicate: NMBPredicate) -> Predicate<NSObject> {
return Predicate { actualExpression in
let result = objcPredicate.satisfies(({ try! actualExpression.evaluate() }),
let result = objcPredicate.satisfies(({ try actualExpression.evaluate() }),
location: actualExpression.location)
return result.toSwift()
}
@@ -30,13 +30,13 @@ internal struct ObjCMatcherWrapper: Matcher {
// Equivalent to Expectation, but for Nimble's Objective-C interface
public class NMBExpectation: NSObject {
internal let _actualBlock: () -> 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) {
@objc public init(actualBlock: @escaping () -> NSObject?, negative: Bool, file: FileString, line: UInt) {
self._actualBlock = actualBlock
self._negative = negative
self._file = file
@@ -3,8 +3,8 @@ import Foundation
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
// swiftlint:disable line_length
public typealias MatcherBlock = (_ actualExpression: Expression<NSObject>, _ failureMessage: FailureMessage) -> Bool
public typealias FullMatcherBlock = (_ actualExpression: Expression<NSObject>, _ failureMessage: FailureMessage, _ shouldNotMatch: Bool) -> Bool
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 {
@@ -24,7 +24,7 @@ public class NMBObjCMatcher: NSObject, NMBMatcher {
public convenience init(canMatchNil: Bool, matcher: @escaping MatcherBlock) {
self.init(canMatchNil: canMatchNil, matcher: matcher, notMatcher: ({ actualExpression, failureMessage in
return !matcher(actualExpression, failureMessage)
return try !matcher(actualExpression, failureMessage)
}))
}
@@ -34,9 +34,9 @@ public class NMBObjCMatcher: NSObject, NMBMatcher {
public convenience init(canMatchNil: Bool, matcher: @escaping FullMatcherBlock) {
self.init(canMatchNil: canMatchNil, matcher: ({ actualExpression, failureMessage in
return matcher(actualExpression, failureMessage, false)
return try matcher(actualExpression, failureMessage, false)
}), notMatcher: ({ actualExpression, failureMessage in
return matcher(actualExpression, failureMessage, true)
return try matcher(actualExpression, failureMessage, true)
}))
}
@@ -55,11 +55,16 @@ public class NMBObjCMatcher: NSObject, NMBMatcher {
return true
}
public func matches(_ actualBlock: @escaping () -> NSObject!, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
public func matches(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
let expr = Expression(expression: actualBlock, location: location)
let result = _match(
expr,
failureMessage)
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 {
@@ -67,11 +72,16 @@ public class NMBObjCMatcher: NSObject, NMBMatcher {
}
}
public func doesNotMatch(_ actualBlock: @escaping () -> NSObject!, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
public func doesNotMatch(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
let expr = Expression(expression: actualBlock, location: location)
let result = _doesNotMatch(
expr,
failureMessage)
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 {
@@ -42,12 +42,23 @@ class NimbleXCTestUnavailableHandler: AssertionHandler {
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
@@ -61,7 +72,7 @@ func isXCTestAvailable() -> Bool {
#endif
}
private func recordFailure(_ message: String, location: SourceLocation) {
public func recordFailure(_ message: String, location: SourceLocation) {
#if SWIFT_PACKAGE
XCTFail("\(message)", file: location.file, line: location.line)
#else
+1 -1
View File
@@ -111,6 +111,6 @@ internal func blockedRunLoopErrorMessageFor(_ fnName: String, leeway: TimeInterv
///
/// 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 = 1, file: FileString = #file, line: UInt = #line, action: @escaping (@escaping () -> Void) -> Void) {
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)
}
+14 -21
View File
@@ -1,23 +1,5 @@
import Foundation
// Deprecated
internal func expressionMatches<T, U>(_ expression: Expression<T>, matcher: U, to: String, description: String?) -> (Bool, FailureMessage)
where U: Matcher, U.ValueType == T {
let msg = FailureMessage()
msg.userDescription = description
msg.to = to
do {
let pass = try matcher.matches(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)
}
}
// Deprecated
internal func expressionDoesNotMatch<T, U>(_ expression: Expression<T>, matcher: U, toNot: String, description: String?) -> (Bool, FailureMessage)
where U: Matcher, U.ValueType == T {
@@ -75,6 +57,10 @@ 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)
@@ -85,8 +71,15 @@ public struct Expectation<T> {
/// 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) = expressionMatches(expression, matcher: matcher, to: "to", description: description)
verify(pass, msg)
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.
@@ -127,6 +120,6 @@ public struct Expectation<T> {
}
// see:
// - AsyncMatcherWrapper for extension
// - `async` for extension
// - NMBExpectation for Objective-C interface
}
+3 -1
View File
@@ -152,8 +152,10 @@ public indirect enum ExpectationMessage {
// Backwards compatibility: converts ExpectationMessage tree to FailureMessage
internal func update(failureMessage: FailureMessage) {
switch self {
case let .fail(msg):
case let .fail(msg) where !msg.isEmpty:
failureMessage.stringValue = msg
case .fail:
break
case let .expectedTo(msg):
failureMessage.actualValue = nil
failureMessage.postfixMessage = msg
+3 -3
View File
@@ -68,7 +68,7 @@ extension NMBObjCMatcher {
@objc public class func allPassMatcher(_ matcher: NMBMatcher) -> NMBPredicate {
return NMBPredicate { actualExpression in
let location = actualExpression.location
let actualValue = try! actualExpression.evaluate()
let actualValue = try actualExpression.evaluate()
var nsObjects = [NSObject]()
var collectionIsUsable = true
@@ -99,7 +99,7 @@ extension NMBObjCMatcher {
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()
return predicate.satisfies(({ try expr.evaluate() }), location: expr.location).toSwift()
} else {
let failureMessage = FailureMessage()
let result = matcher.matches(
@@ -114,7 +114,7 @@ extension NMBObjCMatcher {
)
}
})
return try! pred.satisfies(expr).toObjectiveC()
return try pred.satisfies(expr).toObjectiveC()
}
}
}
@@ -32,76 +32,7 @@ private func async<T>(style: ExpectationStyle, predicate: Predicate<T>, timeout:
// 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 toEventually(...).")
}
}
}
// Deprecated
internal struct AsyncMatcherWrapper<T, U>: Matcher
where U: Matcher, U.ValueType == T {
let fullMatcher: U
let timeoutInterval: TimeInterval
let pollInterval: TimeInterval
init(fullMatcher: U, timeoutInterval: TimeInterval = AsyncDefaults.Timeout, pollInterval: TimeInterval = AsyncDefaults.PollInterval) {
self.fullMatcher = fullMatcher
self.timeoutInterval = timeoutInterval
self.pollInterval = pollInterval
}
func matches(_ actualExpression: Expression<T>, failureMessage: FailureMessage) -> Bool {
let uncachedExpression = actualExpression.withoutCaching()
let fnName = "expect(...).toEventually(...)"
let result = pollBlock(
pollInterval: pollInterval,
timeoutInterval: timeoutInterval,
file: actualExpression.location.file,
line: actualExpression.location.line,
fnName: fnName) {
try self.fullMatcher.matches(uncachedExpression, failureMessage: failureMessage)
}
switch result {
case let .completed(isSuccessful): return isSuccessful
case .timedOut: return false
case let .errorThrown(error):
failureMessage.stringValue = "an unexpected error thrown: <\(error)>"
return false
case let .raisedException(exception):
failureMessage.stringValue = "an unexpected exception thrown: <\(exception)>"
return false
case .blockedRunLoop:
failureMessage.postfixMessage += " (timed out, but main thread was unresponsive)."
return false
case .incomplete:
internalError("Reached .incomplete state for toEventually(...).")
}
}
func doesNotMatch(_ actualExpression: Expression<T>, failureMessage: FailureMessage) -> Bool {
let uncachedExpression = actualExpression.withoutCaching()
let result = pollBlock(
pollInterval: pollInterval,
timeoutInterval: timeoutInterval,
file: actualExpression.location.file,
line: actualExpression.location.line,
fnName: "expect(...).toEventuallyNot(...)") {
try self.fullMatcher.doesNotMatch(uncachedExpression, failureMessage: failureMessage)
}
switch result {
case let .completed(isSuccessful): return isSuccessful
case .timedOut: return false
case let .errorThrown(error):
failureMessage.stringValue = "an unexpected error thrown: <\(error)>"
return false
case let .raisedException(exception):
failureMessage.stringValue = "an unexpected exception thrown: <\(exception)>"
return false
case .blockedRunLoop:
failureMessage.postfixMessage += " (timed out, but main thread was unresponsive)."
return false
case .incomplete:
internalError("Reached .incomplete state for toEventuallyNot(...).")
internalError("Reached .incomplete state for \(fnName)(...).")
}
}
}
@@ -182,14 +113,19 @@ extension Expectation {
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) = expressionMatches(
let (pass, msg) = execute(
expression,
matcher: AsyncMatcherWrapper(
fullMatcher: matcher,
timeoutInterval: timeout,
pollInterval: pollInterval),
.toMatch,
async(
style: .toMatch,
predicate: matcher.predicate,
timeout: timeout,
poll: pollInterval,
fnName: "toEventually"
),
to: "to eventually",
description: description
description: description,
captureExceptions: false
)
verify(pass, msg)
} else {
@@ -208,10 +144,13 @@ extension Expectation {
if expression.isClosure {
let (pass, msg) = expressionDoesNotMatch(
expression,
matcher: AsyncMatcherWrapper(
fullMatcher: matcher,
timeoutInterval: timeout,
pollInterval: pollInterval),
matcher: async(
style: .toNotMatch,
predicate: matcher.predicate,
timeout: timeout,
poll: pollInterval,
fnName: "toEventuallyNot"
),
toNot: "to eventually not",
description: description
)
+2 -2
View File
@@ -59,8 +59,8 @@ public func beAKindOf(_ expectedClass: AnyClass) -> Predicate<NSObject> {
extension NMBObjCMatcher {
@objc public class func beAKindOfMatcher(_ expected: AnyClass) -> NMBMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
return try! beAKindOf(expected).matches(actualExpression, failureMessage: failureMessage)
return NMBPredicate { actualExpression in
return try beAKindOf(expected).satisfies(actualExpression).toObjectiveC()
}
}
}
@@ -48,8 +48,8 @@ public func beAnInstanceOf(_ expectedClass: AnyClass) -> Predicate<NSObject> {
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func beAnInstanceOfMatcher(_ expected: AnyClass) -> NMBMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
return try! beAnInstanceOf(expected).matches(actualExpression, failureMessage: failureMessage)
return NMBPredicate { actualExpression in
return try beAnInstanceOf(expected).satisfies(actualExpression).toObjectiveC()
}
}
}
+16 -4
View File
@@ -43,22 +43,34 @@ public class NMBObjCBeCloseToMatcher: NSObject, NMBMatcher {
_delta = within
}
@objc public func matches(_ actualExpression: @escaping () -> NSObject!, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
@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)
return try! matcher.matches(expr, failureMessage: failureMessage)
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 {
@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)
return try! matcher.doesNotMatch(expr, failureMessage: failureMessage)
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 {
+3 -3
View File
@@ -66,14 +66,14 @@ extension NMBObjCMatcher {
@objc public class func beEmptyMatcher() -> NMBPredicate {
return NMBPredicate { actualExpression in
let location = actualExpression.location
let actualValue = try! actualExpression.evaluate()
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()
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()
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)"
@@ -13,13 +13,13 @@ public func beGreaterThan<T: Comparable>(_ expectedValue: T?) -> Predicate<T> {
/// A Nimble matcher that succeeds when the actual value is greater than the expected value.
public func beGreaterThan(_ expectedValue: NMBComparable?) -> Predicate<NMBComparable> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "be greater than <\(stringify(expectedValue))>"
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 matches
}.requireNonNil
return PredicateStatus(bool: matches)
}
}
public func ><T: Comparable>(lhs: Expectation<T>, rhs: T) {
@@ -35,7 +35,7 @@ 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)
return try beGreaterThan(expected).matches(expr, failureMessage: failureMessage)
}
}
}
@@ -3,25 +3,25 @@ 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> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "be greater than or equal to <\(stringify(expectedValue))>"
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 actual >= expected
return PredicateStatus(bool: actual >= expected)
}
return false
}.requireNonNil
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> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "be greater than or equal to <\(stringify(expectedValue))>"
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 matches
}.requireNonNil
return PredicateStatus(bool: matches)
}
}
public func >=<T: Comparable>(lhs: Expectation<T>, rhs: T) {
@@ -37,7 +37,7 @@ 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)
return try beGreaterThanOrEqualTo(expected).matches(expr, failureMessage: failureMessage)
}
}
}
@@ -3,20 +3,27 @@ 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.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate.define { actualExpression in
#if os(Linux)
let actual = try actualExpression.evaluate() as? AnyObject
#else
let actual = try actualExpression.evaluate() as AnyObject?
#endif
failureMessage.actualValue = "\(identityAsString(actual))"
failureMessage.postfixMessage = "be identical to \(identityAsString(expected))"
let bool: Bool
#if os(Linux)
return actual === (expected as? AnyObject) && actual !== nil
bool = actual === (expected as? AnyObject) && actual !== nil
#else
return actual === (expected as AnyObject?) && actual !== nil
bool = actual === (expected as AnyObject?) && actual !== nil
#endif
}.requireNonNil
return PredicateResult(
bool: bool,
message: .expectedCustomValueTo(
"be identical to \(identityAsString(expected))",
"\(identityAsString(actual))"
)
)
}
}
public func === (lhs: Expectation<Any>, rhs: Any?) {
@@ -39,7 +46,7 @@ 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)
return try beIdenticalTo(expected).matches(aExpr, failureMessage: failureMessage)
}
}
}
+10 -10
View File
@@ -2,23 +2,23 @@ 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> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "be less than <\(stringify(expectedValue))>"
let message = "be less than <\(stringify(expectedValue))>"
return Predicate.simple(message) { actualExpression in
if let actual = try actualExpression.evaluate(), let expected = expectedValue {
return actual < expected
return PredicateStatus(bool: actual < expected)
}
return false
}.requireNonNil
return .fail
}
}
/// A Nimble matcher that succeeds when the actual value is less than the expected value.
public func beLessThan(_ expectedValue: NMBComparable?) -> Predicate<NMBComparable> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "be less than <\(stringify(expectedValue))>"
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 matches
}.requireNonNil
return PredicateStatus(bool: matches)
}
}
public func <<T: Comparable>(lhs: Expectation<T>, rhs: T) {
@@ -34,7 +34,7 @@ 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)
return try beLessThan(expected).matches(expr, failureMessage: failureMessage)
}
}
}
@@ -3,23 +3,22 @@ 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.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "be less than or equal to <\(stringify(expectedValue))>"
return Predicate.simple("be less than or equal to <\(stringify(expectedValue))>") { actualExpression in
if let actual = try actualExpression.evaluate(), let expected = expectedValue {
return actual <= expected
return PredicateStatus(bool: actual <= expected)
}
return false
}.requireNonNil
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.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "be less than or equal to <\(stringify(expectedValue))>"
return Predicate.simple("be less than or equal to <\(stringify(expectedValue))>") { actualExpression in
let actualValue = try actualExpression.evaluate()
return actualValue != nil && actualValue!.NMB_compare(expectedValue) != ComparisonResult.orderedDescending
}.requireNonNil
let matches = actualValue.map { $0.NMB_compare(expectedValue) != .orderedDescending } ?? false
return PredicateStatus(bool: matches)
}
}
public func <=<T: Comparable>(lhs: Expectation<T>, rhs: T) {
@@ -35,7 +34,7 @@ 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)
return try beLessThanOrEqualTo(expected).matches(expr, failureMessage: failureMessage)
}
}
}
+4 -4
View File
@@ -139,28 +139,28 @@ 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)
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)
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)
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)
return try beFalse().matches(expr, failureMessage: failureMessage)
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ public func beNil<T>() -> Predicate<T> {
extension NMBObjCMatcher {
@objc public class func beNilMatcher() -> NMBObjCMatcher {
return NMBObjCMatcher { actualExpression, failureMessage in
return try! beNil().matches(actualExpression, failureMessage: failureMessage)
return try beNil().matches(actualExpression, failureMessage: failureMessage)
}
}
}
+2 -3
View File
@@ -2,10 +2,9 @@ import Foundation
/// A Nimble matcher that succeeds when the actual value is Void.
public func beVoid() -> Predicate<()> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "be void"
return Predicate.simpleNilable("be void") { actualExpression in
let actualValue: ()? = try actualExpression.evaluate()
return actualValue != nil
return PredicateStatus(bool: actualValue != nil)
}
}
+3 -3
View File
@@ -46,13 +46,13 @@ public func beginWith(_ startingSubstring: String) -> Predicate<String> {
extension NMBObjCMatcher {
@objc public class func beginWithMatcher(_ expected: Any) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let actual = try! actualExpression.evaluate()
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)
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)
return try beginWith(expected).matches(expr, failureMessage: failureMessage)
}
}
}
+24 -24
View File
@@ -8,15 +8,15 @@ public func contain<S: Sequence, T: Equatable>(_ items: T...) -> Predicate<S>
public func contain<S: Sequence, T: Equatable>(_ items: [T]) -> Predicate<S>
where S.Iterator.Element == T {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "contain <\(arrayAsString(items))>"
return Predicate.simple("contain <\(arrayAsString(items))>") { actualExpression in
if let actual = try actualExpression.evaluate() {
return items.all {
let matches = items.all {
return actual.contains($0)
}
return PredicateStatus(bool: matches)
}
return false
}.requireNonNil
return .fail
}
}
/// A Nimble matcher that succeeds when the actual string contains the expected substring.
@@ -25,16 +25,16 @@ public func contain(_ substrings: String...) -> Predicate<String> {
}
public func contain(_ substrings: [String]) -> Predicate<String> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "contain <\(arrayAsString(substrings))>"
return Predicate.simple("contain <\(arrayAsString(substrings))>") { actualExpression in
if let actual = try actualExpression.evaluate() {
return substrings.all {
let matches = substrings.all {
let range = actual.range(of: $0)
return range != nil && !range!.isEmpty
}
return PredicateStatus(bool: matches)
}
return false
}.requireNonNil
return .fail
}
}
/// A Nimble matcher that succeeds when the actual string contains the expected substring.
@@ -43,13 +43,13 @@ public func contain(_ substrings: NSString...) -> Predicate<NSString> {
}
public func contain(_ substrings: [NSString]) -> Predicate<NSString> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "contain <\(arrayAsString(substrings))>"
return Predicate.simple("contain <\(arrayAsString(substrings))>") { actualExpression in
if let actual = try actualExpression.evaluate() {
return substrings.all { actual.range(of: $0.description).length != 0 }
let matches = substrings.all { actual.range(of: $0.description).length != 0 }
return PredicateStatus(bool: matches)
}
return false
}.requireNonNil
return .fail
}
}
/// A Nimble matcher that succeeds when the actual collection contains the expected object.
@@ -58,13 +58,13 @@ public func contain(_ items: Any?...) -> Predicate<NMBContainer> {
}
public func contain(_ items: [Any?]) -> Predicate<NMBContainer> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "contain <\(arrayAsString(items))>"
guard let actual = try actualExpression.evaluate() else { return false }
return items.all { item in
return item != nil && actual.contains(item!)
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
}
}.requireNonNil
return PredicateStatus(bool: matches)
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
@@ -72,16 +72,16 @@ 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()
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)
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)
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)"
@@ -2,34 +2,33 @@ import Foundation
public func containElementSatisfying<S: Sequence, T>(_ predicate: @escaping ((T) -> Bool), _ predicateDescription: String = "") -> Predicate<S> where S.Iterator.Element == T {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.actualValue = nil
return Predicate.define { actualExpression in
let message: ExpectationMessage
if predicateDescription == "" {
failureMessage.postfixMessage = "find object in collection that satisfies predicate"
message = .expectedTo("find object in collection that satisfies predicate")
} else {
failureMessage.postfixMessage = "find object in collection \(predicateDescription)"
message = .expectedTo("find object in collection \(predicateDescription)")
}
if let sequence = try actualExpression.evaluate() {
for object in sequence {
if predicate(object) {
return true
return PredicateResult(bool: true, message: message)
}
}
return false
return PredicateResult(bool: false, message: message)
}
return false
}.requireNonNil
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()
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"
@@ -51,8 +50,7 @@ public func containElementSatisfying<S: Sequence, T>(_ predicate: @escaping ((T)
}
failureMessage.actualValue = nil
failureMessage.postfixMessage = ""
failureMessage.to = "to find object in collection that satisfies predicate"
failureMessage.postfixMessage = "find object in collection that satisfies predicate"
return false
}
}
+17 -21
View File
@@ -4,9 +4,7 @@ import Foundation
/// is equal to the expected value.
public func endWith<S: Sequence, T: Equatable>(_ endingElement: T) -> Predicate<S>
where S.Iterator.Element == T {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "end with <\(endingElement)>"
return Predicate.simple("end with <\(endingElement)>") { actualExpression in
if let actualValue = try actualExpression.evaluate() {
var actualGenerator = actualValue.makeIterator()
var lastItem: T?
@@ -16,55 +14,53 @@ public func endWith<S: Sequence, T: Equatable>(_ endingElement: T) -> Predicate<
item = actualGenerator.next()
} while(item != nil)
return lastItem == endingElement
return PredicateStatus(bool: lastItem == endingElement)
}
return false
}.requireNonNil
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.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "end with <\(endingElement)>"
guard let collection = try actualExpression.evaluate() else { return false }
guard collection.count > 0 else { return false }
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 false
return .fail
}
#else
let collectionValue = collection.object(at: collection.count - 1) as AnyObject
#endif
return collectionValue.isEqual(endingElement)
}.requireNonNil
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.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "end with <\(endingSubstring)>"
return Predicate.simple("end with <\(endingSubstring)>") { actualExpression in
if let collection = try actualExpression.evaluate() {
return collection.hasSuffix(endingSubstring)
return PredicateStatus(bool: collection.hasSuffix(endingSubstring))
}
return false
}.requireNonNil
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()
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)
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)
return try endWith(expected).matches(expr, failureMessage: failureMessage)
}
}
}
+2 -2
View File
@@ -212,8 +212,8 @@ public func !=<T, C: Equatable>(lhs: Expectation<[T: C]>, rhs: [T: C]?) {
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func equalMatcher(_ expected: NSObject) -> NMBMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
return try! equal(expected).matches(actualExpression, failureMessage: failureMessage)
return NMBPredicate { actualExpression in
return try equal(expected).satisfies(actualExpression).toObjectiveC()
}
}
}
+23 -17
View File
@@ -8,33 +8,39 @@ import Foundation
/// 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.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate.define { actualExpression in
if let actualValue = try actualExpression.evaluate() {
// swiftlint:disable:next line_length
failureMessage.postfixMessage = "have \(prettyCollectionType(actualValue)) with count \(stringify(expectedValue))"
let message = ExpectationMessage
.expectedCustomValueTo(
"have \(prettyCollectionType(actualValue)) with count \(stringify(expectedValue))",
"\(actualValue.count)"
)
.appended(details: "Actual Value: \(stringify(actualValue))")
let result = expectedValue == actualValue.count
failureMessage.actualValue = "\(actualValue.count)"
failureMessage.extendedMessage = "Actual Value: \(stringify(actualValue))"
return result
return PredicateResult(bool: result, message: message)
} else {
return false
return PredicateResult(status: .fail, message: .fail(""))
}
}.requireNonNil
}
}
/// A Nimble matcher that succeeds when the actual collection's count equals
/// the expected value
public func haveCount(_ expectedValue: Int) -> Predicate<NMBCollection> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate { actualExpression in
if let actualValue = try actualExpression.evaluate() {
// swiftlint:disable:next line_length
failureMessage.postfixMessage = "have \(prettyCollectionType(actualValue)) with count \(stringify(expectedValue))"
let message = ExpectationMessage
.expectedCustomValueTo(
"have \(prettyCollectionType(actualValue)) with count \(stringify(expectedValue))",
"\(actualValue.count)"
)
.appended(details: "Actual Value: \(stringify(actualValue))")
let result = expectedValue == actualValue.count
failureMessage.actualValue = "\(actualValue.count)"
failureMessage.extendedMessage = "Actual Value: \(stringify(actualValue))"
return result
return PredicateResult(bool: result, message: message)
} else {
return false
return PredicateResult(status: .fail, message: .fail(""))
}
}
}
@@ -44,10 +50,10 @@ 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()
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)
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)))"
+7 -8
View File
@@ -3,26 +3,25 @@ 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.fromDeprecatedClosure { actualExpression, failureMessage in
failureMessage.postfixMessage = "match <\(stringify(expectedValue))>"
return Predicate.simple("match <\(stringify(expectedValue))>") { actualExpression in
if let actual = try actualExpression.evaluate() {
if let regexp = expectedValue {
return actual.range(of: regexp, options: .regularExpression) != nil
let bool = actual.range(of: regexp, options: .regularExpression) != nil
return PredicateStatus(bool: bool)
}
}
return false
}.requireNonNil
return .fail
}
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func matchMatcher(_ expected: NSString) -> NMBMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
return NMBPredicate { actualExpression in
let actual = actualExpression.cast { $0 as? String }
return try! match(expected.description).matches(actual, failureMessage: failureMessage)
return try match(expected.description).satisfies(actual).toObjectiveC()
}
}
}
+32 -14
View File
@@ -6,16 +6,24 @@ import Foundation
/// 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.fromDeprecatedClosure { actualExpression, failureMessage in
let actualError: Error? = try actualExpression.evaluate()
return Predicate.define { actualExpression in
let actualError = try actualExpression.evaluate()
let failureMessage = FailureMessage()
setFailureMessageForError(
failureMessage,
postfixMessageVerb: "match",
actualError: actualError,
error: error
)
setFailureMessageForError(failureMessage, postfixMessageVerb: "match", actualError: actualError, error: error)
var matches = false
if let actualError = actualError, errorMatchesExpectedError(actualError, expectedError: error) {
matches = true
}
return matches
}.requireNonNil
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
/// A Nimble matcher that succeeds when the actual expression evaluates to an
@@ -24,35 +32,45 @@ public func matchError<T: Error>(_ error: T) -> Predicate<Error> {
/// 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.fromDeprecatedClosure { actualExpression, failureMessage in
let actualError: Error? = try actualExpression.evaluate()
return Predicate.define { actualExpression in
let actualError = try actualExpression.evaluate()
setFailureMessageForError(failureMessage, postfixMessageVerb: "match", actualError: actualError, error: error)
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 matches
}.requireNonNil
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.fromDeprecatedClosure { actualExpression, failureMessage in
let actualError: Error? = try actualExpression.evaluate()
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 matches
}.requireNonNil
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
@@ -31,8 +31,8 @@ extension Matcher {
#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
func matches(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool
func doesNotMatch(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool
}
#endif
@@ -74,7 +74,8 @@ public func postNotifications<T>(
let collector = NotificationCollector(notificationCenter: center)
collector.startObserving()
var once: Bool = false
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate { actualExpression in
let collectorNotificationsExpression = Expression(memoizedExpression: { _ in
return collector.observedNotifications
}, location: actualExpression.location, withoutCaching: true)
@@ -85,12 +86,13 @@ public func postNotifications<T>(
_ = 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 match
return PredicateResult(bool: match, message: failureMessage.toExpectationMessage())
}
}
+14 -10
View File
@@ -82,8 +82,8 @@ extension Predicate {
}
}
// Question: Should this be exposed? It's safer to not for now and decide later.
internal enum ExpectationStyle {
// The Expectation style intended for comparison to a PredicateStatus.
public enum ExpectationStyle {
case toMatch, toNotMatch
}
@@ -91,9 +91,9 @@ internal enum ExpectationStyle {
/// predicate.
public struct PredicateResult {
/// Status indicates if the predicate matches, does not match, or fails.
var status: PredicateStatus
public var status: PredicateStatus
/// The error message that can be displayed if it does not match
var message: ExpectationMessage
public var message: ExpectationMessage
/// Constructs a new PredicateResult with a given status and error message
public init(status: PredicateStatus, message: ExpectationMessage) {
@@ -108,7 +108,7 @@ public struct PredicateResult {
}
/// Converts the result to a boolean based on what the expectation intended
internal func toBoolean(expectation style: ExpectationStyle) -> Bool {
public func toBoolean(expectation style: ExpectationStyle) -> Bool {
return status.toBoolean(expectation: style)
}
}
@@ -242,7 +242,7 @@ extension Predicate {
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
public typealias PredicateBlock = (_ actualExpression: Expression<NSObject>) -> NMBPredicateResult
public typealias PredicateBlock = (_ actualExpression: Expression<NSObject>) throws -> NMBPredicateResult
public class NMBPredicate: NSObject {
private let predicate: PredicateBlock
@@ -251,20 +251,24 @@ public class NMBPredicate: NSObject {
self.predicate = predicate
}
func satisfies(_ expression: @escaping () -> NSObject!, location: SourceLocation) -> NMBPredicateResult {
func satisfies(_ expression: @escaping () throws -> NSObject?, location: SourceLocation) -> NMBPredicateResult {
let expr = Expression(expression: expression, location: location)
return self.predicate(expr)
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 {
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 {
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)
@@ -17,8 +17,7 @@ public func raiseException(
reason: String? = nil,
userInfo: NSDictionary? = nil,
closure: ((NSException) -> Void)? = nil) -> Predicate<Any> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate { actualExpression in
var exception: NSException?
let capture = NMBExceptionCapture(handler: ({ e in
exception = e
@@ -26,9 +25,9 @@ public func raiseException(
capture.tryBlock {
_ = try! actualExpression.evaluate()
return
}
let failureMessage = FailureMessage()
setFailureMessageForException(
failureMessage,
exception: exception,
@@ -37,13 +36,15 @@ public func raiseException(
userInfo: userInfo,
closure: closure
)
return exceptionMatchesNonNilFieldsOrClosure(
let matches = exceptionMatchesNonNilFieldsOrClosure(
exception,
named: named,
reason: reason,
userInfo: userInfo,
closure: closure
)
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
@@ -129,19 +130,24 @@ public class NMBObjCRaiseExceptionMatcher: NSObject, NMBMatcher {
_block = block
}
@objc public func matches(_ actualBlock: @escaping () -> NSObject!, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
@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)
return try! raiseException(
named: _name,
reason: _reason,
userInfo: _userInfo,
closure: _block
).matches(expr, failureMessage: failureMessage)
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 {
@objc public func doesNotMatch(_ actualBlock: @escaping () -> NSObject?, failureMessage: FailureMessage, location: SourceLocation) -> Bool {
return !matches(actualBlock, failureMessage: failureMessage, location: location)
}
@@ -0,0 +1,76 @@
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
@@ -4,33 +4,11 @@ import Foundation
/// 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)
}
/// Deprecated. Please use `satisfyAnyOf<T>(_) -> Predicate<T>` instead.
internal func satisfyAnyOf<T, U>(_ matchers: [U]) -> Predicate<T>
where U: Matcher, U.ValueType == T {
return NonNilMatcherFunc<T> { actualExpression, failureMessage in
let postfixMessages = NSMutableArray()
var matches = false
for matcher in matchers {
if try matcher.matches(actualExpression, failureMessage: failureMessage) {
matches = true
}
postfixMessages.add(NSString(string: "{\(failureMessage.postfixMessage)}"))
}
failureMessage.postfixMessage = "match one of: " + postfixMessages.componentsJoined(by: ", or ")
if let actualValue = try actualExpression.evaluate() {
failureMessage.actualValue = "\(actualValue)"
}
return matches
}.predicate
return satisfyAnyOf(matchers.map { $0.predicate })
}
internal func satisfyAnyOf<T>(_ predicates: [Predicate<T>]) -> Predicate<T> {
return Predicate { actualExpression in
return Predicate.define { actualExpression in
var postfixMessages = [String]()
var matches = false
for predicate in predicates {
@@ -53,11 +31,8 @@ internal func satisfyAnyOf<T>(_ predicates: [Predicate<T>]) -> Predicate<T> {
)
}
return PredicateResult(
status: PredicateStatus(bool: matches),
message: msg
)
}.requireNonNil
return PredicateResult(bool: matches, message: msg)
}
}
public func || <T>(left: Predicate<T>, right: Predicate<T>) -> Predicate<T> {
@@ -90,7 +65,7 @@ extension NMBObjCMatcher {
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()
return predicate.satisfies({ try expression.evaluate() }, location: actualExpression.location).toSwift()
} else {
let failureMessage = FailureMessage()
// swiftlint:disable:next line_length
@@ -102,7 +77,7 @@ extension NMBObjCMatcher {
elementEvaluators.append(elementEvaluator)
}
return try! satisfyAnyOf(elementEvaluators).satisfies(actualExpression).toObjectiveC()
return try satisfyAnyOf(elementEvaluators).satisfies(actualExpression).toObjectiveC()
}
}
}
@@ -1,13 +1,11 @@
import Foundation
public func throwAssertion() -> Predicate<Void> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate { actualExpression in
#if arch(x86_64) && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
failureMessage.postfixMessage = "throw an assertion"
failureMessage.actualValue = nil
var succeeded = true
let message = ExpectationMessage.expectedTo("throw an assertion")
var actualError: Error?
let caughtException: BadInstructionException? = catchBadInstruction {
#if os(tvOS)
if !NimbleEnvironment.activeInstance.suppressTVOSAssertionWarning {
@@ -27,21 +25,19 @@ public func throwAssertion() -> Predicate<Void> {
#endif
do {
try actualExpression.evaluate()
} catch let error {
succeeded = false
failureMessage.postfixMessage += "; threw error instead <\(error)>"
} catch {
actualError = error
}
}
if !succeeded {
return false
if let actualError = actualError {
return PredicateResult(
bool: false,
message: message.appended(message: "; threw error instead <\(actualError)>")
)
} else {
return PredicateResult(bool: caughtException != nil, message: message)
}
if caughtException == nil {
return false
}
return true
#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" +
+45 -39
View File
@@ -12,22 +12,19 @@ import Foundation
/// nil arguments indicates that the matcher should not attempt to match against
/// that parameter.
public func throwError() -> Predicate<Any> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch let catchedError {
actualError = catchedError
} catch {
actualError = error
}
failureMessage.postfixMessage = "throw any error"
if let actualError = actualError {
failureMessage.actualValue = "<\(actualError)>"
return PredicateResult(bool: true, message: .expectedCustomValueTo("throw any error", "<\(actualError)>"))
} else {
failureMessage.actualValue = "no error"
return PredicateResult(bool: false, message: .expectedCustomValueTo("throw any error", "no error"))
}
return actualError != nil
}
}
@@ -43,15 +40,15 @@ public func throwError() -> Predicate<Any> {
/// 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.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch let catchedError {
actualError = catchedError
} catch {
actualError = error
}
let failureMessage = FailureMessage()
setFailureMessageForError(
failureMessage,
actualError: actualError,
@@ -59,20 +56,23 @@ public func throwError<T: Error>(_ error: T, closure: ((Error) -> Void)? = nil)
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.count > 0 {
if !messages.isEmpty {
matches = false
}
}
}
return matches
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
@@ -88,15 +88,15 @@ public func throwError<T: Error>(_ error: T, closure: ((Error) -> Void)? = nil)
/// 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.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch let catchedError {
actualError = catchedError
} catch {
actualError = error
}
let failureMessage = FailureMessage()
setFailureMessageForError(
failureMessage,
actualError: actualError,
@@ -104,6 +104,7 @@ public func throwError<T: Error & Equatable>(_ error: T, closure: ((T) -> Void)?
errorType: nil,
closure: closure
)
var matches = false
if let actualError = actualError as? T, error == actualError {
matches = true
@@ -113,12 +114,13 @@ public func throwError<T: Error & Equatable>(_ error: T, closure: ((T) -> Void)?
closure(actualError)
}
let messages = assertions.map { $0.message }
if messages.count > 0 {
if !messages.isEmpty {
matches = false
}
}
}
return matches
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
@@ -136,15 +138,15 @@ public func throwError<T: Error & Equatable>(_ error: T, closure: ((T) -> Void)?
public func throwError<T: Error>(
errorType: T.Type,
closure: ((T) -> Void)? = nil) -> Predicate<Any> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch let catchedError {
actualError = catchedError
} catch {
actualError = error
}
let failureMessage = FailureMessage()
setFailureMessageForError(
failureMessage,
actualError: actualError,
@@ -152,16 +154,18 @@ public func throwError<T: Error>(
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.count > 0 {
if !messages.isEmpty {
matches = false
}
}
@@ -176,14 +180,14 @@ public func throwError<T: Error>(
}
}
let messages = assertions.map { $0.message }
if messages.count > 0 {
if !messages.isEmpty {
matches = false
}
}
}
}
return matches
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
@@ -195,15 +199,15 @@ public func throwError<T: Error>(
///
/// The closure only gets called when an error was thrown.
public func throwError(closure: @escaping ((Error) -> Void)) -> Predicate<Any> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch let catchedError {
actualError = catchedError
} catch {
actualError = error
}
let failureMessage = FailureMessage()
setFailureMessageForError(failureMessage, actualError: actualError, closure: closure)
var matches = false
@@ -214,11 +218,12 @@ public func throwError(closure: @escaping ((Error) -> Void)) -> Predicate<Any> {
closure(actualError)
}
let messages = assertions.map { $0.message }
if messages.count > 0 {
if !messages.isEmpty {
matches = false
}
}
return matches
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
@@ -230,15 +235,15 @@ public func throwError(closure: @escaping ((Error) -> Void)) -> Predicate<Any> {
///
/// The closure only gets called when an error was thrown.
public func throwError<T: Error>(closure: @escaping ((T) -> Void)) -> Predicate<Any> {
return Predicate.fromDeprecatedClosure { actualExpression, failureMessage in
return Predicate { actualExpression in
var actualError: Error?
do {
_ = try actualExpression.evaluate()
} catch let catchedError {
actualError = catchedError
} catch {
actualError = error
}
let failureMessage = FailureMessage()
setFailureMessageForError(failureMessage, actualError: actualError, closure: closure)
var matches = false
@@ -249,10 +254,11 @@ public func throwError<T: Error>(closure: @escaping ((T) -> Void)) -> Predicate<
closure(actualError)
}
let messages = assertions.map { $0.message }
if messages.count > 0 {
if !messages.isEmpty {
matches = false
}
}
return matches
return PredicateResult(bool: matches, message: failureMessage.toExpectationMessage())
}
}
@@ -263,7 +263,11 @@ internal class AwaitPromiseBuilder<T> {
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()
+3 -9
View File
@@ -144,7 +144,9 @@ extension Data: TestOutputStringConvertible {
/// will return the result of constructing a string from the value.
///
/// - SeeAlso: `TestOutputStringConvertible`
public func stringify<T>(_ value: T) -> String {
public func stringify<T>(_ value: T?) -> String {
guard let value = value else { return "nil" }
if let value = value as? TestOutputStringConvertible {
return value.testDescription
}
@@ -156,14 +158,6 @@ public func stringify<T>(_ value: T) -> String {
return String(describing: value)
}
/// -SeeAlso: `stringify<T>(value: T)`
public func stringify<T>(_ value: T?) -> String {
if let unboxed = value {
return stringify(unboxed)
}
return "nil"
}
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
@objc public class NMBStringer: NSObject {
@objc public class func stringify(_ obj: Any?) -> String {
@@ -1,9 +0,0 @@
#import <XCTest/XCTest.h>
#import <Nimble/Nimble-Swift.h>
SWIFT_CLASS("_TtC6Nimble22CurrentTestCaseTracker")
@interface CurrentTestCaseTracker : NSObject <XCTestObservation>
+ (CurrentTestCaseTracker *)sharedInstance;
@end
@interface CurrentTestCaseTracker (Register) @end
+6
View File
@@ -350,6 +350,12 @@ NIMBLE_EXPORT id<NMBMatcher> NMB_satisfyAnyOfWithMatchers(id matchers);
#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.
+9 -8
View File
@@ -1,13 +1,10 @@
#import <Nimble/DSL.h>
#if __has_include("Nimble-Swift.h")
#import "Nimble-Swift.h"
#else
#import <Nimble/Nimble-Swift.h>
SWIFT_CLASS("_TtC6Nimble7NMBWait")
@interface NMBWait : NSObject
+ (void)untilTimeout:(NSTimeInterval)timeout file:(NSString *)file line:(NSUInteger)line action:(void (^ _Nonnull)(void (^ _Nonnull)(void)))action;
+ (void)untilFile:(NSString *)file line:(NSUInteger)line action:(void (^ _Nonnull)(void (^ _Nonnull)(void)))action;
@end
#endif
NS_ASSUME_NONNULL_BEGIN
@@ -141,6 +138,10 @@ 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];
}
@@ -16,7 +16,7 @@
return self;
}
- (void)tryBlock:(void(^ _Nonnull)(void))unsafeBlock {
- (void)tryBlock:(__attribute__((noescape)) void(^ _Nonnull)(void))unsafeBlock {
@try {
unsafeBlock();
}
@@ -1,5 +1,10 @@
#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,7 +1,12 @@
#import "CurrentTestCaseTracker.h"
#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.
+1023 -942
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
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>
+9 -4
View File
@@ -85,6 +85,7 @@ extension World {
}
#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'. ")
@@ -100,18 +101,21 @@ extension World {
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'. ")
@@ -131,6 +135,7 @@ extension World {
}
}
@nonobjc
internal func fitBehavesLike(_ name: String, sharedExampleContext: @escaping SharedExampleContext, flags: FilterFlags, file: String, line: UInt) {
var focusedFlags = flags
focusedFlags[Filter.focused] = true
@@ -169,22 +174,22 @@ extension World {
#if (os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) && !SWIFT_PACKAGE
@objc(itWithDescription:flags:file:line:closure:)
private func objc_it(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
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:)
private func objc_fit(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
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:)
private func objc_xit(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
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:)
private func objc_itBehavesLike(_ name: String, sharedExampleContext: @escaping SharedExampleContext, flags: FilterFlags, file: String, line: UInt) {
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
+3
View File
@@ -81,6 +81,9 @@ final public class Example: _ExampleBase {
let exampleMetadata = ExampleMetadata(example: self, exampleIndex: numberOfExamplesRun)
world.currentExampleMetadata = exampleMetadata
defer {
world.currentExampleMetadata = nil
}
world.exampleHooks.executeBefores(exampleMetadata)
group!.phase = .beforesExecuting
@@ -1,8 +1,7 @@
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
import Foundation
public extension NSString {
extension NSString {
private static var invalidCharacters: CharacterSet = {
var invalidCharacters = CharacterSet()
@@ -22,12 +21,29 @@ public extension NSString {
return invalidCharacters
}()
/// This API is not meant to be used outside Quick, so will be unavaialbe in
/// a next major version.
@objc(qck_c99ExtendedIdentifier)
var c99ExtendedIdentifier: String {
public var c99ExtendedIdentifier: String {
let validComponents = components(separatedBy: NSString.invalidCharacters)
let result = validComponents.joined(separator: "_")
return result.isEmpty ? "_" : result
}
}
/// Extension methods or properties for NSObject subclasses are invisible from
/// the Objective-C runtime on static linking unless the consumers add `-ObjC`
/// linker flag, so let's make a wrapper class to mitigate that situation.
///
/// See: https://github.com/Quick/Quick/issues/785 and https://github.com/Quick/Quick/pull/803
@objc
class QCKObjCStringUtils: NSObject {
override private init() {}
@objc
static func c99ExtendedIdentifier(from string: String) -> String {
return string.c99ExtendedIdentifier
}
}
#endif
+1 -1
View File
@@ -160,7 +160,7 @@ final internal class World: _WorldBase {
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
@objc(examplesForSpecClass:)
private func objc_examples(_ specClass: AnyClass) -> [Example] {
internal func objc_examples(_ specClass: AnyClass) -> [Example] {
return examples(specClass)
}
#endif
@@ -1,7 +1,12 @@
#import "QuickConfiguration.h"
#import "World.h"
#import <objc/runtime.h>
#if __has_include("Quick-Swift.h")
#import "Quick-Swift.h"
#else
#import <Quick/Quick-Swift.h>
#endif
typedef void (^QCKClassEnumerationBlock)(Class klass);
/**
+6 -2
View File
@@ -1,6 +1,10 @@
#import "QCKDSL.h"
#import "World.h"
#import "World+DSL.h"
#if __has_include("Quick-Swift.h")
#import "Quick-Swift.h"
#else
#import <Quick/Quick-Swift.h>
#endif
void qck_beforeSuite(QCKDSLEmptyBlock closure) {
[[World sharedWorld] beforeSuite:closure];
@@ -1,20 +0,0 @@
#import <Quick/Quick-Swift.h>
@interface World (SWIFT_EXTENSION(Quick))
- (void)beforeSuite:(void (^ __nonnull)(void))closure;
- (void)afterSuite:(void (^ __nonnull)(void))closure;
- (void)sharedExamples:(NSString * __nonnull)name closure:(void (^ __nonnull)(NSDictionary * __nonnull (^ __nonnull)(void)))closure;
- (void)describe:(NSString * __nonnull)description flags:(NSDictionary * __nonnull)flags closure:(void (^ __nonnull)(void))closure;
- (void)context:(NSString * __nonnull)description flags:(NSDictionary * __nonnull)flags closure:(void (^ __nonnull)(void))closure;
- (void)fdescribe:(NSString * __nonnull)description flags:(NSDictionary * __nonnull)flags closure:(void (^ __nonnull)(void))closure;
- (void)xdescribe:(NSString * __nonnull)description flags:(NSDictionary * __nonnull)flags closure:(void (^ __nonnull)(void))closure;
- (void)beforeEach:(void (^ __nonnull)(void))closure;
- (void)beforeEachWithMetadata:(void (^ __nonnull)(ExampleMetadata * __nonnull))closure;
- (void)afterEach:(void (^ __nonnull)(void))closure;
- (void)afterEachWithMetadata:(void (^ __nonnull)(ExampleMetadata * __nonnull))closure;
- (void)itWithDescription:(NSString * __nonnull)description flags:(NSDictionary * __nonnull)flags file:(NSString * __nonnull)file line:(NSUInteger)line closure:(void (^ __nonnull)(void))closure;
- (void)fitWithDescription:(NSString * __nonnull)description flags:(NSDictionary * __nonnull)flags file:(NSString * __nonnull)file line:(NSUInteger)line closure:(void (^ __nonnull)(void))closure;
- (void)xitWithDescription:(NSString * __nonnull)description flags:(NSDictionary * __nonnull)flags file:(NSString * __nonnull)file line:(NSUInteger)line closure:(void (^ __nonnull)(void))closure;
- (void)itBehavesLikeSharedExampleNamed:(NSString * __nonnull)name sharedExampleContext:(NSDictionary * __nonnull (^ __nonnull)(void))sharedExampleContext flags:(NSDictionary * __nonnull)flags file:(NSString * __nonnull)file line:(NSUInteger)line;
- (void)pending:(NSString * __nonnull)description closure:(void (^ __nonnull)(void))closure;
@end
+6
View File
@@ -47,4 +47,10 @@
*/
- (void)spec;
/**
Returns the currently executing spec. Use in specs that require XCTestCase
methds, e.g. expectationWithDescription.
*/
@property (class, nonatomic, readonly) QuickSpec *current;
@end
+11 -3
View File
@@ -1,7 +1,11 @@
#import "QuickSpec.h"
#import "QuickConfiguration.h"
#import "World.h"
#if __has_include("Quick-Swift.h")
#import "Quick-Swift.h"
#else
#import <Quick/Quick-Swift.h>
#endif
static QuickSpec *currentSpec = nil;
@@ -75,6 +79,10 @@ static QuickSpec *currentSpec = nil;
- (void)spec { }
+ (QuickSpec*) current {
return currentSpec;
}
#pragma mark - Internal Methods
/**
@@ -101,8 +109,8 @@ static QuickSpec *currentSpec = nil;
});
const char *types = [[NSString stringWithFormat:@"%s%s%s", @encode(void), @encode(id), @encode(SEL)] UTF8String];
NSString *originalName = example.name.qck_c99ExtendedIdentifier;
NSString *originalName = [QCKObjCStringUtils c99ExtendedIdentifierFrom:example.name];
NSString *selectorName = originalName;
NSUInteger i = 2;
-18
View File
@@ -1,18 +0,0 @@
#import <Quick/Quick-Swift.h>
@class ExampleGroup;
@class ExampleMetadata;
SWIFT_CLASS("_TtC5Quick5World")
@interface World
@property (nonatomic) ExampleGroup * __nullable currentExampleGroup;
@property (nonatomic) ExampleMetadata * __nullable currentExampleMetadata;
@property (nonatomic) BOOL isRunningAdditionalSuites;
+ (World * __nonnull)sharedWorld;
- (void)configure:(void (^ __nonnull)(Configuration * __nonnull))closure;
- (void)finalizeConfiguration;
- (ExampleGroup * __nonnull)rootExampleGroupForSpecClass:(Class __nonnull)cls;
- (NSArray * __nonnull)examplesForSpecClass:(Class __nonnull)specClass;
- (void)performWithCurrentExampleGroup:(ExampleGroup * __nonnull)group closure:(void (^ __nonnull)(void))closure;
@end
@@ -1,6 +1,11 @@
#import <XCTest/XCTest.h>
#import <objc/runtime.h>
#if __has_include("Quick-Swift.h")
#import "Quick-Swift.h"
#else
#import <Quick/Quick-Swift.h>
#endif
@interface XCTestSuite (QuickTestSuiteBuilder)
@end
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>7.0.3</string>
<string>7.3.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+5 -5
View File
@@ -1,12 +1,12 @@
CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Nimble
APPLICATION_EXTENSION_API_ONLY = YES
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Nimble
ENABLE_BITCODE = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
OTHER_LDFLAGS = -weak-lswiftXCTest -weak_framework "XCTest"
OTHER_LDFLAGS = $(inherited) -Xlinker -no_application_extension -weak-lswiftXCTest -weak_framework "XCTest"
OTHER_SWIFT_FLAGS = $(inherited) -suppress-warnings $(inherited) "-D" "COCOAPODS"
PODS_BUILD_DIR = $BUILD_DIR
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Nimble
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
@@ -1,15 +1,28 @@
#!/bin/sh
set -e
set -u
set -o pipefail
if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
# If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
# frameworks to, so exit 0 (signalling the script phase was successful).
exit 0
fi
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
# Copies and strips a vendored framework
install_framework()
{
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
@@ -58,21 +71,40 @@ install_framework()
fi
}
# Copies the dSYM of a vendored framework
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
if [ -r "$source" ]; then
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}"
# Copy the dSYM into a the targets temp dir.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
local basename
basename="$(basename -s .framework.dSYM "$source")"
binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
# Strip invalid architectures so "fat" simulator / device frameworks work on device
if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then
strip_invalid_archs "$binary"
fi
if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
# Move the stripped file into its final destination.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
else
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM"
fi
fi
}
# Signs a framework with the provided identity
code_sign_if_enabled() {
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
# Use the current code_sign_identitiy
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
code_sign_cmd="$code_sign_cmd &"
@@ -85,10 +117,18 @@ code_sign_if_enabled() {
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
# Get architectures for current file
archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
STRIP_BINARY_RETVAL=0
return
fi
stripped=""
for arch in $archs; do
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary" || exit 1
@@ -98,6 +138,7 @@ strip_invalid_archs() {
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=1
}
@@ -1,5 +1,13 @@
#!/bin/sh
set -e
set -u
set -o pipefail
if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then
# If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy
# resources to, so exit 0 (signalling the script phase was successful).
exit 0
fi
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
@@ -12,7 +20,7 @@ XCASSET_FILES=()
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
case "${TARGETED_DEVICE_FAMILY}" in
case "${TARGETED_DEVICE_FAMILY:-}" in
1,2)
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
;;
@@ -92,7 +100,7 @@ if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
fi
rm -f "$RESOURCES_TO_COPY"
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ]
then
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
@@ -102,5 +110,9 @@ then
fi
done <<<"$OTHER_XCASSETS"
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
else
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist"
fi
fi
@@ -1,11 +1,11 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/SwiftAudio"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SwiftAudio"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftAudio/SwiftAudio.framework/Headers"
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftAudio/SwiftAudio.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "SwiftAudio"
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
PODS_BUILD_DIR = $BUILD_DIR
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
@@ -1,11 +1,11 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/SwiftAudio"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SwiftAudio"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftAudio/SwiftAudio.framework/Headers"
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftAudio/SwiftAudio.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "SwiftAudio"
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
PODS_BUILD_DIR = $BUILD_DIR
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
@@ -1,15 +1,28 @@
#!/bin/sh
set -e
set -u
set -o pipefail
if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
# If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
# frameworks to, so exit 0 (signalling the script phase was successful).
exit 0
fi
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
# Copies and strips a vendored framework
install_framework()
{
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
@@ -58,21 +71,40 @@ install_framework()
fi
}
# Copies the dSYM of a vendored framework
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
if [ -r "$source" ]; then
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}"
# Copy the dSYM into a the targets temp dir.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
local basename
basename="$(basename -s .framework.dSYM "$source")"
binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
# Strip invalid architectures so "fat" simulator / device frameworks work on device
if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then
strip_invalid_archs "$binary"
fi
if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
# Move the stripped file into its final destination.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
else
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM"
fi
fi
}
# Signs a framework with the provided identity
code_sign_if_enabled() {
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
# Use the current code_sign_identitiy
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
code_sign_cmd="$code_sign_cmd &"
@@ -85,10 +117,18 @@ code_sign_if_enabled() {
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
# Get architectures for current file
archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
STRIP_BINARY_RETVAL=0
return
fi
stripped=""
for arch in $archs; do
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary" || exit 1
@@ -98,6 +138,7 @@ strip_invalid_archs() {
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=1
}
@@ -1,5 +1,13 @@
#!/bin/sh
set -e
set -u
set -o pipefail
if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then
# If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy
# resources to, so exit 0 (signalling the script phase was successful).
exit 0
fi
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
@@ -12,7 +20,7 @@ XCASSET_FILES=()
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
case "${TARGETED_DEVICE_FAMILY}" in
case "${TARGETED_DEVICE_FAMILY:-}" in
1,2)
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
;;
@@ -92,7 +100,7 @@ if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
fi
rm -f "$RESOURCES_TO_COPY"
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ]
then
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
@@ -102,5 +110,9 @@ then
fi
done <<<"$OTHER_XCASSETS"
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
else
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist"
fi
fi
@@ -1,11 +1,11 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PLATFORM_DIR)/Developer/Library/Frameworks "$PODS_CONFIGURATION_BUILD_DIR/Nimble" "$PODS_CONFIGURATION_BUILD_DIR/Quick" "$PODS_CONFIGURATION_BUILD_DIR/SwiftAudio"
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PLATFORM_DIR)/Developer/Library/Frameworks "${PODS_CONFIGURATION_BUILD_DIR}/Nimble" "${PODS_CONFIGURATION_BUILD_DIR}/Quick" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftAudio"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Nimble/Nimble.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Quick/Quick.framework/Headers" $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftAudio/SwiftAudio.framework/Headers"
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Nimble/Nimble.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Quick/Quick.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftAudio/SwiftAudio.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "Nimble" -framework "Quick"
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
PODS_BUILD_DIR = $BUILD_DIR
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
@@ -1,11 +1,11 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PLATFORM_DIR)/Developer/Library/Frameworks "$PODS_CONFIGURATION_BUILD_DIR/Nimble" "$PODS_CONFIGURATION_BUILD_DIR/Quick" "$PODS_CONFIGURATION_BUILD_DIR/SwiftAudio"
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PLATFORM_DIR)/Developer/Library/Frameworks "${PODS_CONFIGURATION_BUILD_DIR}/Nimble" "${PODS_CONFIGURATION_BUILD_DIR}/Quick" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftAudio"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Nimble/Nimble.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Quick/Quick.framework/Headers" $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftAudio/SwiftAudio.framework/Headers"
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Nimble/Nimble.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Quick/Quick.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftAudio/SwiftAudio.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "Nimble" -framework "Quick"
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
PODS_BUILD_DIR = $BUILD_DIR
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.2.0</string>
<string>1.3.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+5 -5
View File
@@ -1,12 +1,12 @@
CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Quick
APPLICATION_EXTENSION_API_ONLY = YES
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Quick
ENABLE_BITCODE = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
OTHER_LDFLAGS = -framework "XCTest"
OTHER_LDFLAGS = $(inherited) -Xlinker -no_application_extension -framework "XCTest"
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
PODS_BUILD_DIR = $BUILD_DIR
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Quick
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<string>0.3.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -1,9 +1,8 @@
CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SwiftAudio
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftAudio
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
PODS_BUILD_DIR = $BUILD_DIR
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+55 -35
View File
@@ -13,6 +13,14 @@
0707130B2067F2E000F789B3 /* QueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0707130A2067F2E000F789B3 /* QueueViewController.swift */; };
0707130F2067F40A00F789B3 /* QueueTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0707130D2067F40A00F789B3 /* QueueTableViewCell.swift */; };
070713102067F40A00F789B3 /* QueueTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0707130E2067F40A00F789B3 /* QueueTableViewCell.xib */; };
0708ED6C2116DA4C00EB29BD /* AudioSessionControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0708ED6B2116DA4B00EB29BD /* AudioSessionControllerTests.swift */; };
0708ED702116E89900EB29BD /* Source.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0708ED6F2116E89900EB29BD /* Source.swift */; };
0708ED722116E91D00EB29BD /* Source.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0708ED6F2116E89900EB29BD /* Source.swift */; };
0708ED742116EE0100EB29BD /* AudioPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0708ED732116EE0100EB29BD /* AudioPlayerTests.swift */; };
0708ED79211732F500EB29BD /* TestSound.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 0708ED78211732F500EB29BD /* TestSound.m4a */; };
0708ED7A211732F500EB29BD /* TestSound.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 0708ED78211732F500EB29BD /* TestSound.m4a */; };
07194D212127F6DB002EA8C8 /* ShortTestSound.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 07194D1F2127F283002EA8C8 /* ShortTestSound.m4a */; };
07194D222127F6E9002EA8C8 /* ShortTestSound.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 07194D1F2127F283002EA8C8 /* ShortTestSound.m4a */; };
074A6483205C155E0083D868 /* AVPlayerTimeObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074A6482205C155E0083D868 /* AVPlayerTimeObserverTests.swift */; };
074A6485205C29920083D868 /* AVPlayerItemNotificationObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074A6484205C29920083D868 /* AVPlayerItemNotificationObserverTests.swift */; };
074A6487205E59B60083D868 /* AVPlayerWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074A6486205E59B60083D868 /* AVPlayerWrapperTests.swift */; };
@@ -21,7 +29,9 @@
07732654205ECA8B00C4D1CD /* WAV-MP3.wav in Resources */ = {isa = PBXBuildFile; fileRef = 07732650205EACA300C4D1CD /* WAV-MP3.wav */; };
07732655205ECE1C00C4D1CD /* nasa_throttle_up.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 07732652205EB1B500C4D1CD /* nasa_throttle_up.mp3 */; };
0775575920668B020002C6A1 /* QueueManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0775575820668B020002C6A1 /* QueueManagerTests.swift */; };
07756B69218A4E870023935E /* AudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07756B68218A4E870023935E /* AudioSession.swift */; };
078C908F210D263200555E80 /* AVPlayerItemObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 078C908D210D25F700555E80 /* AVPlayerItemObserverTests.swift */; };
07DBB1E1212C17E600BB4278 /* QueuedAudioPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07DBB1E0212C17E600BB4278 /* QueuedAudioPlayerTests.swift */; };
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; };
607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
@@ -47,13 +57,20 @@
0707130A2067F2E000F789B3 /* QueueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueViewController.swift; sourceTree = "<group>"; };
0707130D2067F40A00F789B3 /* QueueTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueTableViewCell.swift; sourceTree = "<group>"; };
0707130E2067F40A00F789B3 /* QueueTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QueueTableViewCell.xib; sourceTree = "<group>"; };
0708ED6B2116DA4B00EB29BD /* AudioSessionControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSessionControllerTests.swift; sourceTree = "<group>"; };
0708ED6F2116E89900EB29BD /* Source.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Source.swift; sourceTree = "<group>"; };
0708ED732116EE0100EB29BD /* AudioPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerTests.swift; sourceTree = "<group>"; };
0708ED78211732F500EB29BD /* TestSound.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = TestSound.m4a; sourceTree = "<group>"; };
07194D1F2127F283002EA8C8 /* ShortTestSound.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = ShortTestSound.m4a; sourceTree = "<group>"; };
074A6482205C155E0083D868 /* AVPlayerTimeObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerTimeObserverTests.swift; sourceTree = "<group>"; };
074A6484205C29920083D868 /* AVPlayerItemNotificationObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerItemNotificationObserverTests.swift; sourceTree = "<group>"; };
074A6486205E59B60083D868 /* AVPlayerWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerWrapperTests.swift; sourceTree = "<group>"; };
07732650205EACA300C4D1CD /* WAV-MP3.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = "WAV-MP3.wav"; sourceTree = "<group>"; };
07732652205EB1B500C4D1CD /* nasa_throttle_up.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = nasa_throttle_up.mp3; sourceTree = "<group>"; };
0775575820668B020002C6A1 /* QueueManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueManagerTests.swift; sourceTree = "<group>"; };
07756B68218A4E870023935E /* AudioSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSession.swift; sourceTree = "<group>"; };
078C908D210D25F700555E80 /* AVPlayerItemObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerItemObserverTests.swift; sourceTree = "<group>"; };
07DBB1E0212C17E600BB4278 /* QueuedAudioPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueuedAudioPlayerTests.swift; sourceTree = "<group>"; };
521F3AEC1228A2FA2637355F /* Pods-SwiftAudio_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftAudio_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftAudio_Tests/Pods-SwiftAudio_Tests.debug.xcconfig"; sourceTree = "<group>"; };
607FACD01AFB9204008FA782 /* SwiftAudio_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftAudio_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -67,7 +84,7 @@
607FACEB1AFB9204008FA782 /* AVPlayerObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerObserverTests.swift; sourceTree = "<group>"; };
768DA07BCD292FA8C3F43CF5 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
928EC55949C3B1093DA7BC4C /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
B88D29AD5BC56C1834C04294 /* SwiftAudio.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = SwiftAudio.podspec; path = ../SwiftAudio.podspec; sourceTree = "<group>"; };
B88D29AD5BC56C1834C04294 /* SwiftAudio.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = SwiftAudio.podspec; path = ../SwiftAudio.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
C344B34C66182CD1C5AD6DD2 /* Pods-SwiftAudio_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftAudio_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftAudio_Example/Pods-SwiftAudio_Example.debug.xcconfig"; sourceTree = "<group>"; };
C4C4423F4BAFCE03507BC386 /* Pods-SwiftAudio_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftAudio_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftAudio_Tests/Pods-SwiftAudio_Tests.release.xcconfig"; sourceTree = "<group>"; };
C8489AD5161CEA9C48D7DF42 /* Pods_SwiftAudio_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftAudio_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -95,6 +112,26 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0708ED712116E91300EB29BD /* Source */ = {
isa = PBXGroup;
children = (
07194D1F2127F283002EA8C8 /* ShortTestSound.m4a */,
0708ED6F2116E89900EB29BD /* Source.swift */,
07732650205EACA300C4D1CD /* WAV-MP3.wav */,
07732652205EB1B500C4D1CD /* nasa_throttle_up.mp3 */,
0708ED78211732F500EB29BD /* TestSound.m4a */,
);
path = Source;
sourceTree = "<group>";
};
07756B67218A4E640023935E /* Mocks */ = {
isa = PBXGroup;
children = (
07756B68218A4E870023935E /* AudioSession.swift */,
);
path = Mocks;
sourceTree = "<group>";
};
607FACC71AFB9204008FA782 = {
isa = PBXGroup;
children = (
@@ -146,14 +183,17 @@
607FACE81AFB9204008FA782 /* Tests */ = {
isa = PBXGroup;
children = (
07756B67218A4E640023935E /* Mocks */,
0708ED732116EE0100EB29BD /* AudioPlayerTests.swift */,
607FACEB1AFB9204008FA782 /* AVPlayerObserverTests.swift */,
074A6482205C155E0083D868 /* AVPlayerTimeObserverTests.swift */,
074A6484205C29920083D868 /* AVPlayerItemNotificationObserverTests.swift */,
074A6486205E59B60083D868 /* AVPlayerWrapperTests.swift */,
0775575820668B020002C6A1 /* QueueManagerTests.swift */,
078C908D210D25F700555E80 /* AVPlayerItemObserverTests.swift */,
07732650205EACA300C4D1CD /* WAV-MP3.wav */,
07732652205EB1B500C4D1CD /* nasa_throttle_up.mp3 */,
0708ED6B2116DA4B00EB29BD /* AudioSessionControllerTests.swift */,
07DBB1E0212C17E600BB4278 /* QueuedAudioPlayerTests.swift */,
0708ED712116E91300EB29BD /* Source */,
607FACE91AFB9204008FA782 /* Supporting Files */,
);
path = Tests;
@@ -209,7 +249,6 @@
607FACCD1AFB9204008FA782 /* Frameworks */,
607FACCE1AFB9204008FA782 /* Resources */,
92F611A7E298DE7BCA0B0B61 /* [CP] Embed Pods Frameworks */,
D90D8AB1C4A6EFC8D653227D /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -229,7 +268,6 @@
607FACE21AFB9204008FA782 /* Frameworks */,
607FACE31AFB9204008FA782 /* Resources */,
D41B1A785DE273F72BD47633 /* [CP] Embed Pods Frameworks */,
EF21C94415B1482F4674E093 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -296,6 +334,8 @@
607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */,
607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */,
07732655205ECE1C00C4D1CD /* nasa_throttle_up.mp3 in Resources */,
07194D222127F6E9002EA8C8 /* ShortTestSound.m4a in Resources */,
0708ED79211732F500EB29BD /* TestSound.m4a in Resources */,
070713102067F40A00F789B3 /* QueueTableViewCell.xib in Resources */,
07732654205ECA8B00C4D1CD /* WAV-MP3.wav in Resources */,
607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */,
@@ -306,6 +346,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
07194D212127F6DB002EA8C8 /* ShortTestSound.m4a in Resources */,
0708ED7A211732F500EB29BD /* TestSound.m4a in Resources */,
07732653205EB1B500C4D1CD /* nasa_throttle_up.mp3 in Resources */,
07732651205EACA300C4D1CD /* WAV-MP3.wav in Resources */,
);
@@ -370,36 +412,6 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftAudio_Tests/Pods-SwiftAudio_Tests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
D90D8AB1C4A6EFC8D653227D /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftAudio_Example/Pods-SwiftAudio_Example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
EF21C94415B1482F4674E093 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftAudio_Tests/Pods-SwiftAudio_Tests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
F22DFA58013500305552C406 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -428,6 +440,7 @@
0707130B2067F2E000F789B3 /* QueueViewController.swift in Sources */,
070713072067EB4F00F789B3 /* Double + Extensions.swift in Sources */,
607FACD81AFB9204008FA782 /* ViewController.swift in Sources */,
0708ED722116E91D00EB29BD /* Source.swift in Sources */,
0707130F2067F40A00F789B3 /* QueueTableViewCell.swift in Sources */,
070713092067EFFB00F789B3 /* AudioController.swift in Sources */,
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */,
@@ -438,9 +451,14 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
07756B69218A4E870023935E /* AudioSession.swift in Sources */,
0708ED702116E89900EB29BD /* Source.swift in Sources */,
0708ED742116EE0100EB29BD /* AudioPlayerTests.swift in Sources */,
0775575920668B020002C6A1 /* QueueManagerTests.swift in Sources */,
074A6483205C155E0083D868 /* AVPlayerTimeObserverTests.swift in Sources */,
078C908F210D263200555E80 /* AVPlayerItemObserverTests.swift in Sources */,
0708ED6C2116DA4C00EB29BD /* AudioSessionControllerTests.swift in Sources */,
07DBB1E1212C17E600BB4278 /* QueuedAudioPlayerTests.swift in Sources */,
074A6485205C29920083D868 /* AVPlayerItemNotificationObserverTests.swift in Sources */,
607FACEC1AFB9204008FA782 /* AVPlayerObserverTests.swift in Sources */,
074A6487205E59B60083D868 /* AVPlayerWrapperTests.swift in Sources */,
@@ -590,6 +608,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
@@ -607,6 +626,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
<dict>
<key>BuildSystemType</key>
<string>Original</string>
</dict>
</plist>
+11 -8
View File
@@ -13,26 +13,29 @@ import SwiftAudio
class AudioController {
static let shared = AudioController()
let player = QueuedAudioPlayer()
let player: QueuedAudioPlayer
let audioSessionController = AudioSessionController.shared
let sources: [AudioItem] = [
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/67b51d90ffddd6bb3f095059997021b589845f81?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "33 \"GOD\"", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/081447adc23dad4f79ba4f1082615d1c56edf5e1?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "8 (circle)", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/6f9999d909b017eabef97234dd7a206355720d9d?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "715 - CRΣΣKS", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/bf9bdd403c67fdbe06a582e7b292487c8cfd1f7e?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "____45_____", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI"))
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/67b51d90ffddd6bb3f095059997021b589845f81?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "33 \"GOD\"", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/081447adc23dad4f79ba4f1082615d1c56edf5e1?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "8 (circle)", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/6f9999d909b017eabef97234dd7a206355720d9d?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "715 - CRΣΣKS", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/bf9bdd403c67fdbe06a582e7b292487c8cfd1f7e?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "____45_____", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI"))
]
init() {
let controller = RemoteCommandController()
player = QueuedAudioPlayer(remoteCommandController: controller)
player.remoteCommands = [
.stop,
.play,
.pause,
.togglePlayPause,
.skipForward(preferredIntervals: [30]),
.skipBackward(preferredIntervals: [30]),
.next,
.previous,
.changePlaybackPosition
]
try? audioSessionController.set(category: .playback)
try? audioSessionController.activateSession()
try? player.add(items: sources, playWhenReady: false)
}
+2 -2
View File
@@ -46,7 +46,7 @@ extension QueueViewController: UITableViewDataSource, UITableViewDelegate {
case 0:
return 1
case 1:
return controller.player.nextItems?.count ?? 0
return controller.player.nextItems.count ?? 0
default:
return 0
}
@@ -60,7 +60,7 @@ extension QueueViewController: UITableViewDataSource, UITableViewDelegate {
case 0:
item = controller.player.currentItem
case 1:
item = controller.player.nextItems?[indexPath.row]
item = controller.player.nextItems[indexPath.row]
default:
item = nil
}
+7 -4
View File
@@ -31,6 +31,9 @@ class ViewController: UIViewController {
}
@IBAction func togglePlay(_ sender: Any) {
if (!controller.audioSessionController.audioSessionIsActive) {
try? controller.audioSessionController.activateSession()
}
try? controller.player.togglePlaying()
}
@@ -58,6 +61,10 @@ class ViewController: UIViewController {
}
extension ViewController: AudioPlayerDelegate {
func audioPlayer(itemPlaybackEndedWithReason reason: PlaybackEndedReason) {
}
func audioPlayer(playerDidChangeState state: AVPlayerWrapperState) {
playButton.setTitle(state == .playing ? "Pause" : "Play", for: .normal)
@@ -86,10 +93,6 @@ extension ViewController: AudioPlayerDelegate {
}
}
func audioPlayerItemDidComplete() {
}
func audioPlayer(secondsElapsed seconds: Double) {
if !isScrubbing {
slider.setValue(Float(seconds), animated: false)
@@ -15,7 +15,7 @@ class AVPlayerItemNotificationObserverTests: QuickSpec {
var observer: AVPlayerItemNotificationObserver!
beforeEach {
item = AVPlayerItem(asset: AVURLAsset(url: URL(string: "https://p.scdn.co/mp3-preview/4839b070015ab7d6de9fec1756e1f3096d908fba")!))
item = AVPlayerItem(url: URL(fileURLWithPath: Source.path))
observer = AVPlayerItemNotificationObserver()
}
@@ -4,8 +4,6 @@ import AVFoundation
@testable import SwiftAudio
let source = Bundle.main.path(forResource: "WAV-MP3", ofType: "wav")!
class AVPlayerItemObserverTests: QuickSpec {
override func spec() {
@@ -19,7 +17,7 @@ class AVPlayerItemObserverTests: QuickSpec {
context("when observing", {
var item: AVPlayerItem!
beforeEach {
item = AVPlayerItem(url: URL(fileURLWithPath: source))
item = AVPlayerItem(url: URL(fileURLWithPath: Source.path))
observer.startObserving(item: item)
}
@@ -36,7 +34,7 @@ class AVPlayerItemObserverTests: QuickSpec {
context("when observing", {
var item: AVPlayerItem!
beforeEach {
item = AVPlayerItem(url: URL(fileURLWithPath: source))
item = AVPlayerItem(url: URL(fileURLWithPath: Source.path))
observer.startObserving(item: item)
}
it("should be observing", closure: {
+1 -1
View File
@@ -34,7 +34,7 @@ class AVPlayerObserverTests: QuickSpec, AVPlayerObserverDelegate {
context("when player has started", {
beforeEach {
player.replaceCurrentItem(with: AVPlayerItem(asset: AVURLAsset(url: URL(string: "https://p.scdn.co/mp3-preview/4839b070015ab7d6de9fec1756e1f3096d908fba")!)))
player.replaceCurrentItem(with: AVPlayerItem(url: URL(fileURLWithPath: Source.path)))
player.play()
}
+100 -33
View File
@@ -1,35 +1,34 @@
import Quick
import Nimble
import AVFoundation
@testable import SwiftAudio
class AVPlayerWrapperTests: QuickSpec {
override func spec() {
let source = Bundle.main.path(forResource: "WAV-MP3", ofType: "wav")!
let shortSource = Bundle.main.path(forResource: "nasa_throttle_up", ofType: "mp3")!
describe("An AVPlayerWrapper") {
var wrapper: AVPlayerWrapper!
beforeEach {
wrapper = AVPlayerWrapper()
wrapper.automaticallyWaitsToMinimizeStalling = false
wrapper.volume = 0.0
let player = AVPlayer()
player.automaticallyWaitsToMinimizeStalling = false
player.volume = 0.0
wrapper = AVPlayerWrapper(avPlayer: player)
wrapper.bufferDuration = 0.0001
}
describe("state", {
describe("its state", {
it("should be idle", closure: {
expect(wrapper.state).to(equal(AVPlayerWrapperState.idle))
})
context("when loading a source", {
beforeEach {
try? wrapper.load(fromFilePath: source, playWhenReady: false)
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: false)
}
it("should be loading", closure: {
@@ -43,7 +42,7 @@ class AVPlayerWrapperTests: QuickSpec {
context("when playing with no source", {
beforeEach {
try? wrapper.play()
wrapper.play()
}
it("should be idle", closure: {
expect(wrapper.state).to(equal(AVPlayerWrapperState.idle))
@@ -52,7 +51,7 @@ class AVPlayerWrapperTests: QuickSpec {
context("when playing a source", {
beforeEach {
try? wrapper.load(fromFilePath: source, playWhenReady: true)
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: true)
}
it("should eventually be playing", closure: {
@@ -63,16 +62,16 @@ class AVPlayerWrapperTests: QuickSpec {
context("when pausing the source", {
let holder = AudioPlayerDelegateHolder()
let holder = AVPlayerWrapperDelegateHolder()
beforeEach {
wrapper.delegate = holder
holder.stateUpdate = { (state) in
if state == .playing {
try? wrapper.pause()
wrapper.pause()
}
}
try? wrapper.load(fromFilePath: source, playWhenReady: true)
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: true)
}
it("should eventually be paused", closure: {
@@ -81,28 +80,28 @@ class AVPlayerWrapperTests: QuickSpec {
})
context("when toggling the source from play", {
let holder = AudioPlayerDelegateHolder()
let holder = AVPlayerWrapperDelegateHolder()
beforeEach {
wrapper.delegate = holder
holder.stateUpdate = { (state) in
if state == .playing {
try? wrapper.togglePlaying()
wrapper.togglePlaying()
}
}
try? wrapper.load(fromFilePath: source, playWhenReady: true)
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: true)
}
it("should eventually be playing", closure: {
it("should eventually be paused", closure: {
expect(wrapper.state).toEventually(equal(AVPlayerWrapperState.paused))
})
})
context("when stopping the source", {
var holder: AudioPlayerDelegateHolder!
var holder: AVPlayerWrapperDelegateHolder!
var receivedIdleUpdate: Bool = false
beforeEach {
holder = AudioPlayerDelegateHolder()
holder = AVPlayerWrapperDelegateHolder()
wrapper.delegate = holder
holder.stateUpdate = { (state) in
if state == .playing {
@@ -112,7 +111,7 @@ class AVPlayerWrapperTests: QuickSpec {
receivedIdleUpdate = true
}
}
try? wrapper.load(fromFilePath: source, playWhenReady: true)
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: true)
}
it("should eventually be 'idle'", closure: {
@@ -123,7 +122,7 @@ class AVPlayerWrapperTests: QuickSpec {
context("when seeking before loading", {
beforeEach {
try? wrapper.seek(to: 10)
wrapper.seek(to: 10)
}
it("should be idle", closure: {
expect(wrapper.state).to(equal(AVPlayerWrapperState.idle))
@@ -138,26 +137,94 @@ class AVPlayerWrapperTests: QuickSpec {
context("when loading source", {
beforeEach {
try? wrapper.load(fromFilePath: source, playWhenReady: false)
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: false)
}
it("should eventually not be 0", closure: {
expect(wrapper.duration).toEventuallyNot(equal(0))
})
})
})
describe("its current time", {
it("should be 0", closure: {
expect(wrapper.currentTime).to(equal(0))
})
context("when seeking to a time", {
var passed = false
let holder = AVPlayerWrapperDelegateHolder()
let seekTime: TimeInterval = 0.5
beforeEach {
wrapper.delegate = holder
holder.seekCompletion = { passed = true }
wrapper.load(from: Source.url, playWhenReady: false)
wrapper.seek(to: seekTime)
}
it("should eventually be equal to the seeked time", closure: {
expect(passed).toEventually(beTrue())
})
})
})
describe("its rate", {
it("should be 0", closure: {
expect(wrapper.rate).to(equal(0.0))
})
context("when playing a source", {
beforeEach {
wrapper.load(from: URL(fileURLWithPath: Source.path), playWhenReady: true)
}
it("should eventually be 1.0", closure: {
expect(wrapper.rate).toEventually(equal(1.0))
})
})
})
describe("its automaticallyWaitsToMinimizeStalling option", {
it("should be false", closure: {
expect(wrapper.automaticallyWaitsToMinimizeStalling).to(beFalse())
})
context("when setting it to true", {
beforeEach {
wrapper.automaticallyWaitsToMinimizeStalling = true
}
it("should be true", closure: {
expect(wrapper.automaticallyWaitsToMinimizeStalling).to(beTrue())
})
})
})
describe("its timeEventFrequency", {
context("when updated", {
beforeEach {
wrapper.timeEventFrequency = .everyHalfSecond
}
it("should update the playerTimeObservers periodicObserverTimeInterval", closure: {
expect(wrapper.playerTimeObserver.periodicObserverTimeInterval).to(equal(TimeEventFrequency.everyHalfSecond.getTime()))
})
})
})
}
}
}
class AudioPlayerDelegateHolder: AVPlayerWrapperDelegate {
class AVPlayerWrapperDelegateHolder: AVPlayerWrapperDelegate {
func AVWrapper(itemPlaybackDoneWithReason reason: PlaybackEndedReason) {
}
var state: AVPlayerWrapperState? {
didSet {
print(state)
if let state = state {
self.stateUpdate?(state)
}
@@ -171,10 +238,6 @@ class AudioPlayerDelegateHolder: AVPlayerWrapperDelegate {
self.state = state
}
func AVWrapperItemDidComplete() {
}
func AVWrapper(secondsElapsed seconds: Double) {
}
@@ -183,12 +246,16 @@ class AudioPlayerDelegateHolder: AVPlayerWrapperDelegate {
}
var seekCompletion: (() -> Void)?
func AVWrapper(seekTo seconds: Int, didFinish: Bool) {
seekCompletion?()
}
func AVWrapper(didUpdateDuration duration: Double) {
if let state = self.state {
self.stateUpdate?(state)
}
}
}
+200
View File
@@ -0,0 +1,200 @@
import Quick
import Nimble
@testable import SwiftAudio
class AudioPlayerTests: QuickSpec {
override func spec() {
describe("An AudioPlayer") {
var audioPlayer: AudioPlayer!
beforeEach {
audioPlayer = AudioPlayer()
audioPlayer.bufferDuration = 0.0001
audioPlayer.automaticallyWaitsToMinimizeStalling = false
audioPlayer.volume = 0.0
}
describe("its state", {
it("should be idle", closure: {
expect(audioPlayer.playerState).to(equal(AudioPlayerState.idle))
})
context("when audio item is loaded", {
beforeEach {
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
}
it("it should eventually be ready", closure: {
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.ready))
})
})
context("when an item is loaded (playWhenReady=true)", {
beforeEach {
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true)
}
it("it should eventually be playing", closure: {
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.playing))
})
})
context("when playing an item", {
var holder: AudioPlayerDelegateHolder!
beforeEach {
holder = AudioPlayerDelegateHolder()
audioPlayer.delegate = holder
holder.stateUpdate = { state in
print(state.rawValue)
if state == .ready {
try? audioPlayer.play()
}
}
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
}
it("should eventually be playing", closure: {
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.playing))
})
})
context("when pausing an item", {
var holder: AudioPlayerDelegateHolder!
beforeEach {
holder = AudioPlayerDelegateHolder()
audioPlayer.delegate = holder
holder.stateUpdate = { (state) in
if state == .playing {
try? audioPlayer.pause()
}
}
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true)
}
it("should eventually be paused", closure: {
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.paused))
})
})
context("when stopping an item", {
var holder: AudioPlayerDelegateHolder!
beforeEach {
holder = AudioPlayerDelegateHolder()
audioPlayer.delegate = holder
holder.stateUpdate = { (state) in
if state == .playing {
audioPlayer.stop()
}
}
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true)
}
it("should eventually be idle", closure: {
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.idle))
})
})
})
describe("its current time", {
it("should be 0", closure: {
expect(audioPlayer.currentTime).to(equal(0))
})
context("when seeking to a time", {
var passed = false
let holder = AudioPlayerDelegateHolder()
let seekTime: TimeInterval = 0.5
beforeEach {
audioPlayer.delegate = holder
holder.seekCompletion = { passed = true }
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
audioPlayer.seek(to: seekTime)
}
it("should eventually be equal to the seeked time", closure: {
expect(passed).toEventually(beTrue())
})
})
})
describe("its rate", {
it("should be 0", closure: {
expect(audioPlayer.rate).to(equal(0))
})
context("when playing an item", {
beforeEach {
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: true)
}
it("should eventually be 1.0", closure: {
expect(audioPlayer.rate).toEventually(equal(1.0))
})
})
})
describe("its currentItem", {
it("should be nil", closure: {
expect(audioPlayer.currentItem).to(beNil())
})
context("when loading an item", {
beforeEach {
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
}
it("should not be nil", closure: {
expect(audioPlayer.currentItem).toNot(beNil())
})
})
})
}
}
}
class AudioPlayerDelegateHolder: AudioPlayerDelegate {
func audioPlayer(itemPlaybackEndedWithReason reason: PlaybackEndedReason) {
}
var stateUpdate: ((_ state: AudioPlayerState) -> Void)?
var state: AudioPlayerState? {
didSet {
if let state = state {
stateUpdate?(state)
}
}
}
func audioPlayer(playerDidChangeState state: AudioPlayerState) {
self.state = state
}
func audioPlayer(secondsElapsed seconds: Double) {
}
func audioPlayer(failedWithError error: Error?) {
}
var seekCompletion: (() -> Void)?
func audioPlayer(seekTo seconds: Int, didFinish: Bool) {
seekCompletion?()
}
func audioPlayer(didUpdateDuration duration: Double) {
if let state = self.state {
self.stateUpdate?(state)
}
}
}
@@ -0,0 +1,100 @@
import Quick
import Nimble
import AVFoundation
@testable import SwiftAudio
class AudioSessionControllerTests: QuickSpec {
override func spec() {
describe("An AudioSessionController") {
let audioSessionController: AudioSessionController = AudioSessionController(audioSession: NonFailingAudioSession())
it("should be inactive", closure: {
expect(audioSessionController.audioSessionIsActive).to(beFalse())
})
context("when session is activated", {
beforeEach {
try? audioSessionController.activateSession()
}
it("should be active", closure: {
expect(audioSessionController.audioSessionIsActive).to(beTrue())
})
context("when deactivating session", {
beforeEach {
try? audioSessionController.deactivateSession()
}
it("should be inactive", closure: {
expect(audioSessionController.audioSessionIsActive).to(beFalse())
})
})
})
describe("its isObservingForInterruptions", {
it("should be true", closure: {
expect(audioSessionController.isObservingForInterruptions).to(beTrue())
})
context("when isObservingForInterruptions is set to false", {
beforeEach {
audioSessionController.isObservingForInterruptions = false
}
it("should be false", closure: {
expect(audioSessionController.isObservingForInterruptions).to(beFalse())
})
})
})
describe("its delegate", {
context("when a interruption arrives", {
var delegate: AudioSessionControllerDelegateImplementation!
beforeEach {
let notification = Notification(name: .AVAudioSessionInterruption, object: nil, userInfo: [
AVAudioSessionInterruptionTypeKey: UInt(0)
])
delegate = AudioSessionControllerDelegateImplementation()
audioSessionController.delegate = delegate
audioSessionController.handleInterruption(notification: notification)
}
it("should eventually be updated with the interruption type", closure: {
expect(delegate.interruptionType).toEventuallyNot(beNil())
})
})
})
}
describe("An AudioSessionController with a failing AudioSession") {
var audioSessionController: AudioSessionController!
beforeEach {
audioSessionController = AudioSessionController(audioSession: FailingAudioSession())
}
context("when activated", {
beforeEach {
try? audioSessionController.activateSession()
}
it("should be inactive", closure: {
expect(audioSessionController.audioSessionIsActive).to(beFalse())
})
})
}
}
}
class AudioSessionControllerDelegateImplementation: AudioSessionControllerDelegate {
var interruptionType: AVAudioSessionInterruptionType? = nil
func handleInterruption(type: AVAudioSessionInterruptionType) {
self.interruptionType = type
}
}
+54
View File
@@ -0,0 +1,54 @@
//
// AudioSession.swift
// SwiftAudio_Tests
//
// Created by Jørgen Henrichsen on 31/10/2018.
// Copyright © 2018 CocoaPods. All rights reserved.
//
import Foundation
import AVFoundation
@testable import SwiftAudio
class NonFailingAudioSession: AudioSession {
var isOtherAudioPlaying: Bool = false
var availableCategories: [String] = []
func setCategory(_ category: String) throws {}
func setCategory(_ category: String, mode: String, options: AVAudioSessionCategoryOptions) throws {}
func setActive(_ active: Bool) throws {}
func setActive(_ active: Bool, with options: AVAudioSessionSetActiveOptions) throws {}
}
class FailingAudioSession: AudioSession {
var isOtherAudioPlaying: Bool = false
var availableCategories: [String] = []
func setCategory(_ category: String) throws {
throw AVError(AVError.unknown)
}
func setCategory(_ category: String, mode: String, options: AVAudioSessionCategoryOptions) throws {
throw AVError(AVError.unknown)
}
func setActive(_ active: Bool) throws {
throw AVError(AVError.unknown)
}
func setActive(_ active: Bool, with options: AVAudioSessionSetActiveOptions) throws {
throw AVError(AVError.unknown)
}
}
+137 -10
View File
@@ -20,6 +20,57 @@ class QueueManagerTests: QuickSpec {
manager = QueueManager()
}
describe("its current item", {
it("should be nil", closure: {
expect(manager.current).to(beNil())
})
context("when one item is added", closure: {
beforeEach {
manager.addItem(self.dummyItem)
}
it("should not be nil", closure: {
expect(manager.current).toNot(beNil())
})
it("should be the added item", closure: {
expect(manager.current).to(equal(self.dummyItem))
})
context("then replaced", closure: {
beforeEach {
manager.replaceCurrentItem(with: 1)
}
it("should be the new item", closure: {
expect(manager.current).to(equal(1))
})
})
})
context("when replaced", closure: {
beforeEach {
manager.replaceCurrentItem(with: 1)
}
it("should not be nil", closure: {
expect(manager.current).toNot(beNil())
})
})
context("when mulitple items are added", {
beforeEach {
manager.addItems(self.dummyItems)
}
it("should not be nil", closure: {
expect(manager.current).toNot(beNil())
})
})
})
context("when adding one item", {
beforeEach {
@@ -30,9 +81,13 @@ class QueueManagerTests: QuickSpec {
expect(manager.items).notTo(beEmpty())
})
it("should set it as the current item", closure: {
expect(manager.current).toNot(beNil())
expect(manager.current).to(equal(self.dummyItem))
context("then replacing the item", closure: {
beforeEach {
try? manager.replaceCurrentItem(with: 1)
}
it("should have replaced the current item", closure: {
expect(manager.current).to(equal(1))
})
})
context("then calling next", {
@@ -80,7 +135,7 @@ class QueueManagerTests: QuickSpec {
it("should have next items", closure: {
expect(manager.nextItems).toNot(beNil())
expect(manager.nextItems?.count).to(equal(self.dummyItems.count - 1))
expect(manager.nextItems.count).to(equal(self.dummyItems.count - 1))
})
context("then calling next", {
@@ -99,7 +154,7 @@ class QueueManagerTests: QuickSpec {
})
it("should have previous items", closure: {
expect(manager.previousItems.count).to(equal(1))
expect(manager.previousItems).toNot(beNil())
})
context("then calling previous", {
@@ -117,12 +172,58 @@ class QueueManagerTests: QuickSpec {
})
})
context("adding more items", {
var initialItemCount: Int!
let newItems: [Int] = [10, 11, 12, 13]
beforeEach {
initialItemCount = manager.items.count
try? manager.addItems(newItems, at: manager.items.endIndex - 1)
}
it("should have more items", closure: {
expect(manager.items.count).to(equal(initialItemCount + newItems.count))
})
})
context("adding more items at a smaller index than currentIndex", {
var initialCurrentIndex: Int!
let newItems: [Int] = [10, 11, 12, 13]
beforeEach {
initialCurrentIndex = manager.currentIndex
try? manager.addItems(newItems, at: initialCurrentIndex)
}
it("currentIndex should increase by number of new items", closure: {
expect(manager.currentIndex).to(equal(initialCurrentIndex + newItems.count))
})
})
// MARK: - Removal
context("then removing a item with index less than currentIndex", {
beforeEach {
var removed: Int?
var initialCurrentIndex: Int!
beforeEach {
try? manager.jump(to: 3)
initialCurrentIndex = manager.currentIndex
removed = try? manager.removeItem(at: initialCurrentIndex - 1)
}
it("should remove an item", closure: {
expect(removed).toNot(beNil())
})
it("should decrement the currentIndex", closure: {
expect(manager.currentIndex).to(equal(initialCurrentIndex - 1))
})
}
})
context("then removing the second item", {
var removed: Int?
beforeEach {
removed = try? manager.remove(atIndex: 1)
removed = try? manager.removeItem(at: 1)
}
it("should have one less item", closure: {
@@ -134,7 +235,7 @@ class QueueManagerTests: QuickSpec {
context("then removing the last item", {
var removed: Int?
beforeEach {
removed = try? manager.remove(atIndex: self.dummyItems.count - 1)
removed = try? manager.removeItem(at: self.dummyItems.count - 1)
}
it("should have one less item", closure: {
@@ -146,7 +247,7 @@ class QueueManagerTests: QuickSpec {
context("then removing the current item", {
var removed: Int?
beforeEach {
removed = try? manager.remove(atIndex: manager.currentIndex)
removed = try? manager.removeItem(at: manager.currentIndex)
}
it("should not remove any items", closure: {
expect(removed).to(beNil())
@@ -157,7 +258,7 @@ class QueueManagerTests: QuickSpec {
context("then removing with too large index", {
var removed: Int?
beforeEach {
removed = try? manager.remove(atIndex: self.dummyItems.count)
removed = try? manager.removeItem(at: self.dummyItems.count)
}
it("should not remove any items", closure: {
@@ -169,7 +270,7 @@ class QueueManagerTests: QuickSpec {
context("then removing with too small index", {
var removed: Int?
beforeEach {
removed = try? manager.remove(atIndex: -1)
removed = try? manager.removeItem(at: -1)
}
it("should not remove any items", closure: {
@@ -178,6 +279,16 @@ class QueueManagerTests: QuickSpec {
})
})
context("then removing upcoming items", {
beforeEach {
manager.removeUpcomingItems()
}
it("should have no next items", closure: {
expect(manager.nextItems.count).to(equal(0))
})
})
// MARK: - Jumping
context("then jumping to the current item", {
@@ -343,6 +454,22 @@ class QueueManagerTests: QuickSpec {
expect(manager.items).to(equal(afterMoving))
})
})
// MARK: - Clear
context("when queue is cleared", {
beforeEach {
manager.clearQueue()
}
it("should have currentIndex 0", closure: {
expect(manager.currentIndex).to(equal(0))
})
it("should have no items", closure: {
expect(manager.items.count).to(equal(0))
})
})
})
}
}
+131
View File
@@ -0,0 +1,131 @@
import Quick
import Nimble
@testable import SwiftAudio
class QueuedAudioPlayerTests: QuickSpec {
override func spec() {
describe("A QueuedAudioPlayer") {
var audioPlayer: QueuedAudioPlayer!
beforeEach {
audioPlayer = QueuedAudioPlayer()
audioPlayer.bufferDuration = 0.0001
audioPlayer.automaticallyWaitsToMinimizeStalling = false
audioPlayer.volume = 0.0
}
describe("its current item", {
it("should be nil", closure: {
expect(audioPlayer.currentItem).to(beNil())
})
context("when adding one item", {
var item: AudioItem!
beforeEach {
item = ShortSource.getAudioItem()
try? audioPlayer.add(item: item, playWhenReady: false)
}
it("should not be nil", closure: {
expect(audioPlayer.currentItem).toNot(beNil())
})
context("then loading a new item", closure: {
beforeEach {
try? audioPlayer.load(item: Source.getAudioItem(), playWhenReady: false)
}
it("should have replaced the item", closure: {
expect(audioPlayer.currentItem?.getSourceUrl()).toNot(equal(item.getSourceUrl()))
})
})
})
context("when adding multiple items", {
beforeEach {
try? audioPlayer.add(items: [ShortSource.getAudioItem(), ShortSource.getAudioItem()], playWhenReady: false)
}
it("should not be nil", closure: {
expect(audioPlayer.currentItem).toNot(beNil())
})
})
})
describe("its next items", {
it("should be empty", closure: {
expect(audioPlayer.nextItems.count).to(equal(0))
})
context("when adding 2 items", {
beforeEach {
try? audioPlayer.add(items: [Source.getAudioItem(), Source.getAudioItem()])
}
it("should contain 1 item", closure: {
expect(audioPlayer.nextItems.count).to(equal(1))
})
context("then calling next()", {
beforeEach {
try? audioPlayer.next()
}
it("should contain 0 items", closure: {
expect(audioPlayer.nextItems.count).to(equal(0))
})
context("then calling previous()", {
beforeEach {
try? audioPlayer.previous()
}
it("should contain 1 item", closure: {
expect(audioPlayer.nextItems.count).to(equal(1))
})
})
})
context("then removing one item", {
beforeEach {
try? audioPlayer.removeItem(at: 1)
}
it("should be empty", closure: {
expect(audioPlayer.nextItems.count).to(equal(0))
})
})
context("then jumping to the last item", {
beforeEach {
try? audioPlayer.jumpToItem(atIndex: 1)
}
it("should be empty", closure: {
expect(audioPlayer.nextItems.count).to(equal(0))
})
})
})
})
describe("its previous items", {
it("should be empty", closure: {
expect(audioPlayer.previousItems.count).to(equal(0))
})
context("when adding 2 items", {
beforeEach {
try? audioPlayer.add(items: [ShortSource.getAudioItem(), ShortSource.getAudioItem()])
}
it("should be empty", closure: {
expect(audioPlayer.previousItems.count).to(equal(0))
})
context("then calling next()", {
beforeEach {
try? audioPlayer.next()
}
it("should contain one item", closure: {
expect(audioPlayer.previousItems.count).to(equal(1))
})
})
})
})
}
}
}
Binary file not shown.
+28
View File
@@ -0,0 +1,28 @@
//
// Sources.swift
// SwiftAudio_Tests
//
// Created by Jørgen Henrichsen on 05/08/2018.
// Copyright © 2018 CocoaPods. All rights reserved.
//
import Foundation
import SwiftAudio
struct Source {
static let path: String = Bundle.main.path(forResource: "TestSound", ofType: "m4a")!
static let url: URL = URL(fileURLWithPath: Source.path)
static func getAudioItem() -> AudioItem {
return DefaultAudioItem(audioUrl: Source.path, sourceType: .file, pitchAlgorithmType: .lowQualityZeroLatency)
}
}
struct ShortSource {
static let path: String = Bundle.main.path(forResource: "ShortTestSound", ofType: "m4a")!
static let url: URL = URL(fileURLWithPath: Source.path)
static func getAudioItem() -> AudioItem {
return DefaultAudioItem(audioUrl: ShortSource.path, sourceType: .file, pitchAlgorithmType: .lowQualityZeroLatency)
}
}
Binary file not shown.

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