Skip to content

Commit

Permalink
Add SwiftPM support on macOS
Browse files Browse the repository at this point in the history
  • Loading branch information
ikesyo committed Feb 7, 2017
1 parent c1a7490 commit fb15938
Show file tree
Hide file tree
Showing 14 changed files with 267 additions and 33 deletions.
38 changes: 29 additions & 9 deletions .Package.test.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,38 @@ import PackageDescription

let package = Package(
name: "Quick",
targets: {
#if _runtime(_ObjC)
return [
Target(name: "QuickSpecBase"),
Target(name: "Quick", dependencies: [ "QuickSpecBase" ]),
Target(name: "QuickTests", dependencies: [ "Quick" ]),
]
#else
return [
Target(name: "Quick"),
Target(name: "QuickTests", dependencies: [ "Quick" ]),
]
#endif
}(),
// TODO: Once the `test` command has been implemented in the Swift Package Manager, this should be changed to
// be `testDependencies:` instead. For now it has to be done like this for the library to get linked with the test targets.
// See: https://github.com/apple/swift-evolution/blob/master/proposals/0019-package-manager-testing.md
dependencies: [
.Package(url: "https://github.com/Quick/Nimble", majorVersion: 5)
.Package(url: "https://github.com/Quick/Nimble", majorVersion: 6)
],
exclude: [
"Sources/QuickObjectiveC",
"Tests/QuickTests/QuickAfterSuiteTests/AfterSuiteTests+ObjC.m",
"Tests/QuickTests/QuickFocusedTests/FocusedTests+ObjC.m",
"Tests/QuickTests/QuickTests/FunctionalTests/ObjC",
"Tests/QuickTests/QuickTests/Helpers",
"Tests/QuickTests/QuickTests/QuickConfigurationTests.m",
]
exclude: {
var excludes = [
"Sources/QuickObjectiveC",
"Tests/QuickTests/QuickAfterSuiteTests/AfterSuiteTests+ObjC.m",
"Tests/QuickTests/QuickFocusedTests/FocusedTests+ObjC.m",
"Tests/QuickTests/QuickTests/FunctionalTests/ObjC",
"Tests/QuickTests/QuickTests/Helpers",
"Tests/QuickTests/QuickTests/QuickConfigurationTests.m",
]
#if !_runtime(_ObjC)
excludes.append("Sources/QuickSpecBase")
#endif
return excludes
}()
)
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ matrix:
env:
- PLATFORM=tvos
- XCODE_ACTION="build-for-testing test-without-building"
- os: osx
env:
- PLATFORM=swiftpm
- os: osx
sudo: required
env:
Expand Down
2 changes: 1 addition & 1 deletion Externals/Nimble
Submodule Nimble updated 77 files
+36 −0 .github/ISSUE_TEMPLATE
+14 −0 .github/PULL_REQUEST_TEMPLATE
+2 −0 .hound.yml
+16 −0 .swiftlint.yml
+1 −1 LICENSE
+2 −2 Nimble.podspec
+74 −5 Nimble.xcodeproj/project.pbxproj
+105 −2 README.md
+9 −9 Sources/Lib/CwlPreconditionTesting/CwlPreconditionTesting/CwlBadInstructionException.swift
+20 −21 Sources/Lib/CwlPreconditionTesting/CwlPreconditionTesting/CwlCatchBadInstruction.swift
+3 −3 Sources/Lib/CwlPreconditionTesting/CwlPreconditionTesting/CwlCatchBadInstructionPOSIX.swift
+1 −1 Sources/Lib/CwlPreconditionTesting/CwlPreconditionTesting/CwlDarwinDefinitions.swift
+0 −1 Sources/Nimble/Adapters/AssertionDispatcher.swift
+2 −2 Sources/Nimble/Adapters/AssertionRecorder.swift
+3 −3 Sources/Nimble/Adapters/NMBExpectation.swift
+1 −1 Sources/Nimble/Adapters/NMBObjCMatcher.swift
+7 −8 Sources/Nimble/Adapters/NimbleXCTestHandler.swift
+3 −3 Sources/Nimble/DSL+Wait.swift
+5 −10 Sources/Nimble/Expectation.swift
+1 −1 Sources/Nimble/Expression.swift
+1 −1 Sources/Nimble/FailureMessage.swift
+14 −20 Sources/Nimble/Matchers/AllPass.swift
+7 −12 Sources/Nimble/Matchers/AsyncMatcherWrapper.swift
+18 −8 Sources/Nimble/Matchers/BeAKindOf.swift
+15 −5 Sources/Nimble/Matchers/BeAnInstanceOf.swift
+2 −2 Sources/Nimble/Matchers/BeCloseTo.swift
+0 −1 Sources/Nimble/Matchers/BeEmpty.swift
+1 −2 Sources/Nimble/Matchers/BeGreaterThan.swift
+2 −3 Sources/Nimble/Matchers/BeIdenticalTo.swift
+1 −1 Sources/Nimble/Matchers/BeLessThan.swift
+3 −3 Sources/Nimble/Matchers/BeVoid.swift
+1 −3 Sources/Nimble/Matchers/BeginWith.swift
+2 −4 Sources/Nimble/Matchers/Contain.swift
+59 −0 Sources/Nimble/Matchers/ContainElementSatisfying.swift
+2 −5 Sources/Nimble/Matchers/EndWith.swift
+5 −5 Sources/Nimble/Matchers/Equal.swift
+1 −1 Sources/Nimble/Matchers/Match.swift
+2 −2 Sources/Nimble/Matchers/MatcherProtocols.swift
+2 −2 Sources/Nimble/Matchers/PostNotification.swift
+2 −2 Sources/Nimble/Matchers/RaisesException.swift
+10 −13 Sources/Nimble/Matchers/SatisfyAnyOf.swift
+5 −5 Sources/Nimble/Matchers/ThrowAssertion.swift
+2 −2 Sources/Nimble/Matchers/ThrowError.swift
+3 −3 Sources/Nimble/Utils/Async.swift
+1 −2 Sources/Nimble/Utils/Errors.swift
+4 −4 Sources/Nimble/Utils/SourceLocation.swift
+5 −6 Sources/Nimble/Utils/Stringers.swift
+4 −0 Sources/NimbleObjectiveC/DSL.h
+4 −0 Sources/NimbleObjectiveC/DSL.m
+1 −1 Sources/NimbleObjectiveC/NMBExceptionCapture.h
+1 −1 Tests/LinuxMain.swift
+6 −6 Tests/NimbleTests/AsynchronousTest.swift
+1 −1 Tests/NimbleTests/Helpers/XCTestCaseProvider.swift
+1 −1 Tests/NimbleTests/Helpers/utils.swift
+15 −15 Tests/NimbleTests/Matchers/AllPassTest.swift
+56 −23 Tests/NimbleTests/Matchers/BeAKindOfTest.swift
+37 −16 Tests/NimbleTests/Matchers/BeAnInstanceOfTest.swift
+8 −8 Tests/NimbleTests/Matchers/BeCloseToTest.swift
+5 −5 Tests/NimbleTests/Matchers/BeEmptyTest.swift
+1 −1 Tests/NimbleTests/Matchers/BeGreaterThanTest.swift
+4 −4 Tests/NimbleTests/Matchers/BeIdenticalToObjectTest.swift
+1 −1 Tests/NimbleTests/Matchers/BeIdenticalToTest.swift
+7 −7 Tests/NimbleTests/Matchers/BeLogicalTest.swift
+1 −1 Tests/NimbleTests/Matchers/BeNilTest.swift
+86 −0 Tests/NimbleTests/Matchers/ContainElementSatisfyingTest.swift
+1 −1 Tests/NimbleTests/Matchers/ContainTest.swift
+55 −55 Tests/NimbleTests/Matchers/EqualTest.swift
+1 −1 Tests/NimbleTests/Matchers/HaveCountTest.swift
+4 −4 Tests/NimbleTests/Matchers/MatchTest.swift
+1 −1 Tests/NimbleTests/Matchers/RaisesExceptionTest.swift
+4 −4 Tests/NimbleTests/Matchers/SatisfyAnyOfTest.swift
+1 −1 Tests/NimbleTests/Matchers/ThrowAssertionTest.swift
+8 −8 Tests/NimbleTests/Matchers/ThrowErrorTest.swift
+12 −13 Tests/NimbleTests/SynchronousTests.swift
+9 −9 Tests/NimbleTests/UserDescriptionTest.swift
+64 −0 Tests/NimbleTests/objc/ObjCContainElementSatisfying.m
+1 −1 Tests/NimbleTests/objc/ObjCRaiseExceptionTest.m
36 changes: 28 additions & 8 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,32 @@ import PackageDescription

let package = Package(
name: "Quick",
exclude: [
"Sources/QuickObjectiveC",
"Tests/QuickTests/QuickAfterSuiteTests/AfterSuiteTests+ObjC.m",
"Tests/QuickTests/QuickFocusedTests/FocusedTests+ObjC.m",
"Tests/QuickTests/QuickTests/FunctionalTests/ObjC",
"Tests/QuickTests/QuickTests/Helpers",
"Tests/QuickTests/QuickTests/QuickConfigurationTests.m",
]
targets: {
#if _runtime(_ObjC)
return [
Target(name: "QuickSpecBase"),
Target(name: "Quick", dependencies: [ "QuickSpecBase" ]),
Target(name: "QuickTests", dependencies: [ "Quick" ]),
]
#else
return [
Target(name: "Quick"),
Target(name: "QuickTests", dependencies: [ "Quick" ]),
]
#endif
}(),
exclude: {
var excludes = [
"Sources/QuickObjectiveC",
"Tests/QuickTests/QuickAfterSuiteTests/AfterSuiteTests+ObjC.m",
"Tests/QuickTests/QuickFocusedTests/FocusedTests+ObjC.m",
"Tests/QuickTests/QuickTests/FunctionalTests/ObjC",
"Tests/QuickTests/QuickTests/Helpers",
"Tests/QuickTests/QuickTests/QuickConfigurationTests.m",
]
#if !_runtime(_ObjC)
excludes.append("Sources/QuickSpecBase")
#endif
return excludes
}()
)
6 changes: 6 additions & 0 deletions Quick.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,9 @@
96327C621C56E90C00405AB3 /* QuickSpec+QuickSpec_MethodList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "QuickSpec+QuickSpec_MethodList.m"; sourceTree = "<group>"; };
AEB080BB1C72F028004917D3 /* XCTestObservationCenter+QCKSuspendObservation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "XCTestObservationCenter+QCKSuspendObservation.m"; sourceTree = "<group>"; };
AED9C8621CC8A7BD00432F62 /* CrossReferencingSpecs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrossReferencingSpecs.swift; sourceTree = "<group>"; };
CD261AC81DEC8B0000A8863C /* QuickConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickConfiguration.swift; sourceTree = "<group>"; };
CD3451461E4703D4000C8633 /* QuickMain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickMain.swift; sourceTree = "<group>"; };
CD3451471E4703D4000C8633 /* QuickSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickSpec.swift; sourceTree = "<group>"; };
CE57CED81C430BD200D63004 /* NSBundle+CurrentTestBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSBundle+CurrentTestBundle.swift"; sourceTree = "<group>"; };
CE57CED91C430BD200D63004 /* QuickSelectedTestSuiteBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickSelectedTestSuiteBuilder.swift; sourceTree = "<group>"; };
CE57CEDA1C430BD200D63004 /* QuickTestSuite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickTestSuite.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -715,6 +718,7 @@
isa = PBXGroup;
children = (
DA169E4719FF5DF100619816 /* Configuration.swift */,
CD261AC81DEC8B0000A8863C /* QuickConfiguration.swift */,
);
path = Configuration;
sourceTree = "<group>";
Expand Down Expand Up @@ -860,6 +864,8 @@
DA169E4619FF5DF100619816 /* Configuration */,
DA3124E119FCAEE8002858A7 /* DSL */,
DA408BDE19FF5599005DF92A /* Hooks */,
CD3451461E4703D4000C8633 /* QuickMain.swift */,
CD3451471E4703D4000C8633 /* QuickSpec.swift */,
34F375A619515CA700CE1B99 /* World.swift */,
34F3759E19515CA700CE1B99 /* Example.swift */,
DA02C91819A8073100093156 /* ExampleMetadata.swift */,
Expand Down
33 changes: 32 additions & 1 deletion Sources/Quick/Configuration/QuickConfiguration.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@
import Foundation
import XCTest

// NOTE: This file is not intended to be included in the Xcode project or CocoaPods.
// It is picked up by the Swift Package Manager during its build process.

open class QuickConfiguration {
#if SWIFT_PACKAGE

open class QuickConfiguration: NSObject {
open class func configure(_ configuration: Configuration) {}
}

#if _runtime(_ObjC)

internal func qck_enumerateSubclasses<T: AnyObject>(_ klass: T.Type, block: (T.Type) -> Void) {
var classesCount = objc_getClassList(nil, 0)

guard classesCount > 0 else {
return
}

let classes = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(classesCount))
classesCount = objc_getClassList(AutoreleasingUnsafeMutablePointer(classes), classesCount)

var subclass, superclass: AnyClass!
for i in 0..<classesCount {
subclass = classes[Int(i)]
superclass = class_getSuperclass(subclass)
if superclass === klass {
block(subclass as! T.Type) // swiftlint:disable:this force_cast
}
}

free(classes)
}

#endif

#endif
13 changes: 9 additions & 4 deletions Sources/Quick/QuickMain.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import XCTest
#if os(Linux)

// NOTE: This file is not intended to be included in the Xcode project or CocoaPods.
// It is picked up by the Swift Package Manager during its build process.

#if SWIFT_PACKAGE && os(Linux)

/// When using Quick with swift-corelibs-xctest, automatic discovery of specs and
/// configurations is not available. Instead, you should create a standalone
/// executable and call this function from its main.swift file. This will execute
Expand All @@ -20,14 +22,17 @@ import XCTest
public func QCKMain(_ specs: [QuickSpec.Type],
configurations: [QuickConfiguration.Type] = [],
testCases: [XCTestCaseEntry] = []) -> Never {
// Perform all configuration (ensures that shared examples have been discovered)
World.sharedWorld.configure { configuration in
let world = World.sharedWorld

// Perform all configurations (ensures that shared examples have been discovered)
world.configure { configuration in
for configurationClass in configurations {
configurationClass.configure(configuration)
}
}
World.sharedWorld.finalizeConfiguration()
world.finalizeConfiguration()

XCTMain(specs.flatMap { testCase($0.allTests) } + testCases)
}

#endif
78 changes: 76 additions & 2 deletions Sources/Quick/QuickSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@ import XCTest
// NOTE: This file is not intended to be included in the Xcode project or CocoaPods.
// It is picked up by the Swift Package Manager during its build process.

open class QuickSpec: XCTestCase {
#if SWIFT_PACKAGE

#if _runtime(_ObjC)
import QuickSpecBase

public typealias QuickSpecBase = _QuickSpecBase
#else
public typealias QuickSpecBase = XCTestCase
#endif

open class QuickSpec: QuickSpecBase {
open func spec() {}

#if os(Linux)
#if !_runtime(_ObjC)
public required init() {
super.init(name: "", testClosure: { _ in })
}
Expand All @@ -17,6 +27,68 @@ open class QuickSpec: XCTestCase {
public required override init() {
super.init()
}

/// This method is used as a hook for the following two purposes
///
/// 1. Performing all configurations
/// 2. Gathering examples for each spec classes
///
/// On Linux, those are done in `LinuxMain.swift` and `Quick.QCKMain`. But
/// SwiftPM on macOS does not have the mechanism (test cases are automatically
/// discovered powered by Objective-C runtime), so we needed the alternative
/// way.
override open class func defaultTestSuite() -> XCTestSuite {
let world = World.sharedWorld

if !world.isConfigurationFinalized {
// Perform all configurations (ensures that shared examples have been discovered)
world.configure { configuration in
qck_enumerateSubclasses(QuickConfiguration.self) { configurationClass in
configurationClass.configure(configuration)
}
}
world.finalizeConfiguration()
}

// Let's gather examples for each spec classes. This has the same effect
// as listing spec classes in `LinuxMain.swift` on Linux.
_ = allTests

return super.defaultTestSuite()
}

override open class func _testMethodSelectors() -> [_SelectorWrapper] {
let examples = World.sharedWorld.examples(self)

var selectorNames = Set<String>()
return examples.map { example in
let selector = addInstanceMethod(for: example, classSelectorNames: &selectorNames)
return _SelectorWrapper(selector: selector)
}
}

private static func addInstanceMethod(for example: Example, classSelectorNames selectorNames : inout Set<String>) -> Selector {
let block: @convention(block) (QuickSpec) -> Void = { _ in
example.run()
}
let implementation = imp_implementationWithBlock(block as Any)

let originalName = example.name
var selectorName = originalName
var i: UInt = 2

while selectorNames.contains(selectorName) {
selectorName = String(format: "%@_%tu", originalName, i)
i += 1
}

selectorNames.insert(selectorName)

let selector = NSSelectorFromString(selectorName)
class_addMethod(self, selector, implementation, "v@:")

return selector
}
#endif

static var allTestsCache = [String: [(String, (XCTestCase) -> () throws -> Void)]]()
Expand Down Expand Up @@ -46,3 +118,5 @@ open class QuickSpec: XCTestCase {
}
}
}

#endif
3 changes: 2 additions & 1 deletion Sources/Quick/World.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ final internal class World: NSObject {
private var specs: [String: ExampleGroup] = [:]
private var sharedExamples: [String: SharedExampleClosure] = [:]
private let configuration = Configuration()
private var isConfigurationFinalized = false

internal private(set) var isConfigurationFinalized = false

internal var exampleHooks: ExampleHooks {return configuration.exampleHooks }
internal var suiteHooks: SuiteHooks { return configuration.suiteHooks }
Expand Down
55 changes: 55 additions & 0 deletions Sources/QuickSpecBase/QuickSpecBase.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#import "QuickSpecBase.h"

#pragma mark - _SelectorWrapper

@interface _SelectorWrapper ()
@property(nonatomic, assign) SEL selector;
@end

@implementation _SelectorWrapper

- (instancetype)initWithSelector:(SEL)selector {
self = [super init];
_selector = selector;
return self;
}

@end


#pragma mark - _QuickSpecBase

@implementation _QuickSpecBase

- (instancetype)init {
self = [super initWithInvocation: nil];
return self;
}

/**
Invocations for each test method in the test case. QuickSpec overrides this method to define a
new method for each example defined in +[QuickSpec spec].
@return An array of invocations that execute the newly defined example methods.
*/
+ (NSArray<NSInvocation *> *)testInvocations {
NSArray<_SelectorWrapper *> *wrappers = [self _testMethodSelectors];
NSMutableArray<NSInvocation *> *invocations = [NSMutableArray arrayWithCapacity:wrappers.count];

for (_SelectorWrapper *wrapper in wrappers) {
SEL selector = wrapper.selector;
NSMethodSignature *signature = [self instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = selector;

[invocations addObject:invocation];
}

return invocations;
}

+ (NSArray<_SelectorWrapper *> *)_testMethodSelectors {
return @[];
}

@end
11 changes: 11 additions & 0 deletions Sources/QuickSpecBase/include/QuickSpecBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@import Foundation;
@import XCTest;

@interface _SelectorWrapper : NSObject
- (instancetype)initWithSelector:(SEL)selector;
@end

@interface _QuickSpecBase : XCTestCase
+ (NSArray<_SelectorWrapper *> *)_testMethodSelectors;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
@end
4 changes: 2 additions & 2 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ Quick.QCKMain([
Configuration_BeforeEachSpec.self,
FunctionalTests_CrossReferencingSpecA.self,
FunctionalTests_CrossReferencingSpecB.self,
FunctionalTests_FocusedSpec_Focused.self,
FunctionalTests_FocusedSpec_Unfocused.self
_FunctionalTests_FocusedSpec_Focused.self,
_FunctionalTests_FocusedSpec_Unfocused.self
],
configurations: [
FunctionalTests_SharedExamples_BeforeEachTests_SharedExamples.self,
Expand Down
Loading

0 comments on commit fb15938

Please sign in to comment.