@@ -11,6 +11,7 @@ See http://swift.org/CONTRIBUTORS.txt for Swift project authors
1111import class Foundation. ProcessInfo
1212
1313import Basic
14+ import Build
1415import Utility
1516
1617#if HasCustomVersionString
@@ -21,8 +22,9 @@ import func POSIX.chdir
2122import func POSIX. exit
2223
2324private enum TestError : Swift . Error {
24- case testsExecutableNotFound
2525 case invalidListTestJSONData
26+ case multipleTestProducts
27+ case testsExecutableNotFound
2628}
2729
2830extension TestError : CustomStringConvertible {
@@ -32,6 +34,8 @@ extension TestError: CustomStringConvertible {
3234 return " no tests found to execute, create a module in your `Tests' directory "
3335 case . invalidListTestJSONData:
3436 return " Invalid list test JSON structure. "
37+ case . multipleTestProducts:
38+ return " cannot test packages with multiple test products defined "
3539 }
3640 }
3741}
@@ -75,11 +79,16 @@ private func ==(lhs: Mode, rhs: Mode) -> Bool {
7579 return lhs. description == rhs. description
7680}
7781
82+ // FIXME: Merge this with the `swift-build` arguments.
7883private enum TestToolFlag : Argument {
84+ case xcc( String )
85+ case xld( String )
86+ case xswiftc( String )
7987 case chdir( AbsolutePath )
8088 case buildPath( AbsolutePath )
8189 case colorMode( ColorWrap . Mode )
8290 case skipBuild
91+ case ignoreDependencies
8392 case verbose( Int )
8493
8594 init ? ( argument: String , pop: ( ) -> String ? ) throws {
@@ -95,6 +104,12 @@ private enum TestToolFlag: Argument {
95104 self = . verbose( 1 )
96105 case " --skip-build " :
97106 self = . skipBuild
107+ case " -Xcc " :
108+ self = try . xcc( forcePop ( ) )
109+ case " -Xlinker " :
110+ self = try . xld( forcePop ( ) )
111+ case " -Xswiftc " :
112+ self = try . xswiftc( forcePop ( ) )
98113 case " --build-path " :
99114 self = try . buildPath( AbsolutePath ( forcePop ( ) , relativeTo: currentWorkingDirectory) )
100115 case " --color " :
@@ -103,6 +118,8 @@ private enum TestToolFlag: Argument {
103118 throw OptionParserError . invalidUsage ( " invalid color mode: \( rawValue) " )
104119 }
105120 self = . colorMode( mode)
121+ case " --ignore-dependencies " :
122+ self = . ignoreDependencies
106123 default :
107124 return nil
108125 }
@@ -113,6 +130,8 @@ private class TestToolOptions: Options {
113130 var verbosity : Int = 0
114131 var buildTests : Bool = true
115132 var colorMode : ColorWrap . Mode = . Auto
133+ var flags = BuildFlags ( )
134+ var ignoreDependencies : Bool = false
116135}
117136
118137/// swift-test tool namespace
@@ -133,7 +152,7 @@ public struct SwiftTestTool: SwiftTool {
133152 if let dir = opts. chdir {
134153 try chdir ( dir. asString)
135154 }
136-
155+
137156 switch mode {
138157 case . usage:
139158 usage ( )
@@ -146,7 +165,7 @@ public struct SwiftTestTool: SwiftTool {
146165 #endif
147166
148167 case . listTests:
149- let testPath = try determineTestPath ( opts : opts)
168+ let testPath = try buildTestsIfNeeded ( opts)
150169 let testSuites = try getTestSuites ( path: testPath)
151170 // Print the tests.
152171 for testSuite in testSuites {
@@ -158,9 +177,7 @@ public struct SwiftTestTool: SwiftTool {
158177 }
159178
160179 case . run( let specifier) :
161- try buildTestsIfNeeded ( opts)
162- let testPath = try determineTestPath ( opts: opts)
163-
180+ let testPath = try buildTestsIfNeeded ( opts)
164181 let success = test ( path: testPath, xctestArg: specifier)
165182 exit ( success ? 0 : 1 )
166183 }
@@ -172,57 +189,40 @@ public struct SwiftTestTool: SwiftTool {
172189 }
173190 }
174191
175- private let configuration = " debug " //FIXME should swift-test support configuration option?
176-
177192 /// Builds the "test" target if enabled in options.
178- private func buildTestsIfNeeded( _ opts: TestToolOptions ) throws {
179- let yamlPath = opts. path. build. appending ( RelativePath ( " \( configuration) .yaml " ) )
193+ ///
194+ /// - Returns: The path to the test binary.
195+ private func buildTestsIfNeeded( _ opts: TestToolOptions ) throws -> AbsolutePath {
196+ let graph = try loadPackage ( at: opts. path. root, ignoreDependencies: opts. ignoreDependencies)
180197 if opts. buildTests {
181- try build ( yamlPath: yamlPath, target: " test " )
198+ let yaml = try describe ( opts. path. build, configuration, graph, flags: opts. flags, toolchain: UserToolchain ( ) )
199+ try build ( yamlPath: yaml, target: " test " )
182200 }
183- }
184-
185- /// Locates the XCTest bundle on OSX and XCTest executable on Linux.
186- /// First check if <build_path>/debug/<PackageName>Tests.xctest is present, otherwise
187- /// walk the build folder and look for folder/file ending with `.xctest`.
188- ///
189- /// - Parameters:
190- /// - opts: Options object created by parsing the commandline arguments.
191- ///
192- /// - Throws: TestError
193- ///
194- /// - Returns: Path to XCTest bundle (OSX) or executable (Linux).
195- private func determineTestPath( opts: Options ) throws -> AbsolutePath {
196-
197- //FIXME better, ideally without parsing manifest since
198- // that makes us depend on the whole Manifest system
199-
200- let packageName = opts. path. root. basename //FIXME probably not true
201- let maybePath = opts. path. build. appending ( RelativePath ( configuration) ) . appending ( RelativePath ( " \( packageName) Tests.xctest " ) )
202-
203- let possibleTestPath : AbsolutePath
204-
205- if exists ( maybePath) {
206- possibleTestPath = maybePath
207- } else {
208- let possiblePaths = try walk ( opts. path. build) . filter {
209- $0. basename != " Package.xctest " && // this was our hardcoded name, may still exist if no clean
210- $0. suffix == " .xctest "
201+
202+ // See the logic in `PackageLoading`'s `PackageExtensions.swift`.
203+ //
204+ // FIXME: We should also check if the package has any test
205+ // modules, which isn't trivial (yet).
206+ let testProducts = graph. products. filter {
207+ if case . Test = $0. type {
208+ return true
209+ } else {
210+ return false
211211 }
212-
213- guard let path = possiblePaths. first else {
214- throw TestError . testsExecutableNotFound
215- }
216-
217- possibleTestPath = path
218212 }
219-
220- guard isValidTestPath ( possibleTestPath) else {
213+ if testProducts. count == 0 {
221214 throw TestError . testsExecutableNotFound
215+ } else if testProducts. count > 1 {
216+ throw TestError . multipleTestProducts
217+ } else {
218+ return opts. path. build. appending ( RelativePath ( configuration. dirname) ) . appending ( component: testProducts [ 0 ] . name + " .xctest " )
222219 }
223- return possibleTestPath
224220 }
225221
222+ // FIXME: We need to support testing in other build configurations, but need
223+ // to solve the testability problem first.
224+ private let configuration = Build . Configuration. debug
225+
226226 private func usage( _ print: ( String ) -> Void = { print ( $0) } ) {
227227 // .........10.........20.........30.........40.........50.........60.........70..
228228 print ( " OVERVIEW: Build and run tests " )
@@ -238,6 +238,9 @@ public struct SwiftTestTool: SwiftTool {
238238 print ( " --color <mode> Specify color mode (auto|always|never) [default: auto] " )
239239 print ( " -v, --verbose Increase verbosity of informational output " )
240240 print ( " --skip-build Skip building the test target " )
241+ print ( " -Xcc <flag> Pass flag through to all C compiler invocations " )
242+ print ( " -Xlinker <flag> Pass flag through to all linker invocations " )
243+ print ( " -Xswiftc <flag> Pass flag through to all Swift compiler invocations " )
241244 print ( " " )
242245 print ( " NOTE: Use `swift package` to perform other functions on packages " )
243246 }
@@ -252,12 +255,20 @@ public struct SwiftTestTool: SwiftTool {
252255 opts. chdir = path
253256 case . verbose( let amount) :
254257 opts. verbosity += amount
258+ case . xcc( let value) :
259+ opts. flags. cCompilerFlags. append ( value)
260+ case . xld( let value) :
261+ opts. flags. linkerFlags. append ( value)
262+ case . xswiftc( let value) :
263+ opts. flags. swiftCompilerFlags. append ( value)
255264 case . buildPath( let buildPath) :
256265 opts. path. build = buildPath
257266 case . colorMode( let mode) :
258267 opts. colorMode = mode
259268 case . skipBuild:
260269 opts. buildTests = false
270+ case . ignoreDependencies:
271+ opts. ignoreDependencies = true
261272 }
262273 }
263274
@@ -272,8 +283,6 @@ public struct SwiftTestTool: SwiftTool {
272283 ///
273284 /// - Returns: True if execution exited with return code 0.
274285 private func test( path: AbsolutePath , xctestArg: String ? = nil ) -> Bool {
275- precondition ( isValidTestPath ( path) )
276-
277286 var args : [ String ] = [ ]
278287 #if os(macOS)
279288 args = [ " xcrun " , " xctest " ]
@@ -330,9 +339,6 @@ public struct SwiftTestTool: SwiftTool {
330339 ///
331340 /// - Returns: Array of TestSuite
332341 private func getTestSuites( path: AbsolutePath ) throws -> [ TestSuite ] {
333- // Make sure tests are present.
334- guard isValidTestPath ( path) else { throw TestError . testsExecutableNotFound }
335-
336342 // Run the correct tool.
337343 #if os(macOS)
338344 let tempFile = try TemporaryFile ( )
@@ -349,14 +355,6 @@ public struct SwiftTestTool: SwiftTool {
349355 }
350356}
351357
352- private func isValidTestPath( _ path: AbsolutePath ) -> Bool {
353- #if os(macOS)
354- return isDirectory ( path) // ${foo}.xctest is dir on OSX
355- #else
356- return isFile ( path) // otherwise ${foo}.xctest is executable file
357- #endif
358- }
359-
360358/// A struct to hold the XCTestSuite data.
361359struct TestSuite {
362360
0 commit comments