@@ -62,13 +62,20 @@ struct PackageToJS: CommandPlugin {
62
62
63
63
struct TestOptions {
64
64
/// Whether to only build tests, don't run them
65
- var buildOnly : Bool = false
65
+ var buildOnly : Bool
66
+ var listTests : Bool
67
+ var testLibrary : String ?
68
+ var filter : [ String ]
69
+
66
70
var options : Options
67
71
68
72
static func parse( from extractor: inout ArgumentExtractor ) -> TestOptions {
69
73
let buildOnly = extractor. extractFlag ( named: " build-only " )
74
+ let listTests = extractor. extractFlag ( named: " list-tests " )
75
+ let testLibrary = extractor. extractOption ( named: " test-library " ) . last
76
+ let filter = extractor. extractOption ( named: " filter " )
70
77
let options = Options . parse ( from: & extractor)
71
- return TestOptions ( buildOnly: buildOnly != 0 , options: options)
78
+ return TestOptions ( buildOnly: buildOnly != 0 , listTests : listTests != 0 , testLibrary : testLibrary , filter : filter , options: options)
72
79
}
73
80
74
81
static func help( ) -> String {
@@ -84,7 +91,7 @@ struct PackageToJS: CommandPlugin {
84
91
$ swift package --swift-sdk wasm32-unknown-wasi plugin js test
85
92
# Just build tests, don't run them
86
93
$ swift package --swift-sdk wasm32-unknown-wasi plugin js test --build-only
87
- """
94
+ """
88
95
}
89
96
}
90
97
@@ -129,7 +136,9 @@ struct PackageToJS: CommandPlugin {
129
136
"""
130
137
} ) ,
131
138
]
132
- static private func reportBuildFailure( _ build: PackageManager . BuildResult , _ arguments: [ String ] ) {
139
+ static private func reportBuildFailure(
140
+ _ build: PackageManager . BuildResult , _ arguments: [ String ]
141
+ ) {
133
142
for diagnostic in Self . friendlyBuildDiagnostics {
134
143
if let message = diagnostic ( build, arguments) {
135
144
printStderr ( " \n " + message)
@@ -138,11 +147,6 @@ struct PackageToJS: CommandPlugin {
138
147
}
139
148
140
149
func performCommand( context: PluginContext , arguments: [ String ] ) throws {
141
- if arguments. contains ( where: { [ " -h " , " --help " ] . contains ( $0) } ) {
142
- printStderr ( BuildOptions . help ( ) )
143
- return
144
- }
145
-
146
150
if arguments. first == " test " {
147
151
return try performTestCommand ( context: context, arguments: Array ( arguments. dropFirst ( ) ) )
148
152
}
@@ -153,6 +157,11 @@ struct PackageToJS: CommandPlugin {
153
157
static let JAVASCRIPTKIT_PACKAGE_ID : Package . ID = " javascriptkit "
154
158
155
159
func performBuildCommand( context: PluginContext , arguments: [ String ] ) throws {
160
+ if arguments. contains ( where: { [ " -h " , " --help " ] . contains ( $0) } ) {
161
+ printStderr ( BuildOptions . help ( ) )
162
+ return
163
+ }
164
+
156
165
var extractor = ArgumentExtractor ( arguments)
157
166
let buildOptions = BuildOptions . parse ( from: & extractor)
158
167
@@ -185,7 +194,8 @@ struct PackageToJS: CommandPlugin {
185
194
}
186
195
var make = MiniMake ( explain: buildOptions. options. explain)
187
196
let planner = PackagingPlanner (
188
- options: buildOptions. options, context: context, selfPackage: selfPackage, outputDir: outputDir)
197
+ options: buildOptions. options, context: context, selfPackage: selfPackage,
198
+ outputDir: outputDir)
189
199
let rootTask = planner. planBuild (
190
200
make: & make, wasmProductArtifact: productArtifact)
191
201
cleanIfBuildGraphChanged ( root: rootTask, make: make, context: context)
@@ -195,6 +205,11 @@ struct PackageToJS: CommandPlugin {
195
205
}
196
206
197
207
func performTestCommand( context: PluginContext , arguments: [ String ] ) throws {
208
+ if arguments. contains ( where: { [ " -h " , " --help " ] . contains ( $0) } ) {
209
+ printStderr ( TestOptions . help ( ) )
210
+ return
211
+ }
212
+
198
213
var extractor = ArgumentExtractor ( arguments)
199
214
let testOptions = TestOptions . parse ( from: & extractor)
200
215
@@ -227,13 +242,15 @@ struct PackageToJS: CommandPlugin {
227
242
}
228
243
}
229
244
guard let productArtifact = productArtifact else {
230
- throw PackageToJSError ( " Failed to find ' \( productName) .wasm' or ' \( productName) .xctest' " )
231
- }
232
- let outputDir = if let outputPath = testOptions. options. outputPath {
233
- URL ( fileURLWithPath: outputPath)
234
- } else {
235
- context. pluginWorkDirectoryURL. appending ( path: " PackageTests " )
245
+ throw PackageToJSError (
246
+ " Failed to find ' \( productName) .wasm' or ' \( productName) .xctest' " )
236
247
}
248
+ let outputDir =
249
+ if let outputPath = testOptions. options. outputPath {
250
+ URL ( fileURLWithPath: outputPath)
251
+ } else {
252
+ context. pluginWorkDirectoryURL. appending ( path: " PackageTests " )
253
+ }
237
254
guard
238
255
let selfPackage = findPackageInDependencies (
239
256
package : context. package , id: Self . JAVASCRIPTKIT_PACKAGE_ID)
@@ -242,16 +259,47 @@ struct PackageToJS: CommandPlugin {
242
259
}
243
260
var make = MiniMake ( explain: testOptions. options. explain)
244
261
let planner = PackagingPlanner (
245
- options: testOptions. options, context: context, selfPackage: selfPackage, outputDir: outputDir)
246
- let rootTask = planner. planTestBuild (
262
+ options: testOptions. options, context: context, selfPackage: selfPackage,
263
+ outputDir: outputDir)
264
+ let ( rootTask, binDir) = planner. planTestBuild (
247
265
make: & make, wasmProductArtifact: productArtifact)
248
266
cleanIfBuildGraphChanged ( root: rootTask, make: make, context: context)
249
267
print ( " Packaging tests... " )
250
268
try make. build ( output: rootTask)
251
269
print ( " Packaging tests finished " )
270
+
271
+ let testRunner = binDir. appending ( path: " test.js " )
272
+ if !testOptions. buildOnly {
273
+ var extraArguments : [ String ] = [ ]
274
+ if testOptions. listTests {
275
+ extraArguments += [ " --list-tests " ]
276
+ }
277
+ try runTest ( testRunner: testRunner, context: context, extraArguments: extraArguments + testOptions. filter)
278
+ try runTest ( testRunner: testRunner, context: context,
279
+ extraArguments: [ " --testing-library " , " swift-testing " ] + extraArguments + testOptions. filter. flatMap { [ " --filter " , $0] } )
280
+ }
281
+ }
282
+
283
+ private func runTest( testRunner: URL , context: PluginContext , extraArguments: [ String ] ) throws {
284
+ let node = try which ( " node " )
285
+ let arguments = [ " --experimental-wasi-unstable-preview1 " , testRunner. path] + extraArguments
286
+ print ( " Running test... " )
287
+ print ( " $ \( ( [ node. path] + arguments) . map { " \" \( $0) \" " } . joined ( separator: " " ) ) " )
288
+
289
+ let task = Process ( )
290
+ task. executableURL = node
291
+ task. arguments = arguments
292
+ task. currentDirectoryURL = context. pluginWorkDirectoryURL
293
+ try task. run ( )
294
+ task. waitUntilExit ( )
295
+ guard task. terminationStatus == 0 else {
296
+ throw PackageToJSError ( " Test failed with status \( task. terminationStatus) " )
297
+ }
252
298
}
253
299
254
- private func buildWasm( productName: String , context: PluginContext ) throws -> PackageManager . BuildResult {
300
+ private func buildWasm( productName: String , context: PluginContext ) throws
301
+ -> PackageManager . BuildResult
302
+ {
255
303
var parameters = PackageManager . BuildParameters (
256
304
configuration: . inherit,
257
305
logging: . concise
@@ -357,6 +405,23 @@ private func printStderr(_ message: String) {
357
405
fputs ( message + " \n " , stderr)
358
406
}
359
407
408
+ private func which( _ executable: String ) throws -> URL {
409
+ let pathSeparator : Character
410
+ #if os(Windows)
411
+ pathSeparator = " ; "
412
+ #else
413
+ pathSeparator = " : "
414
+ #endif
415
+ let paths = ProcessInfo . processInfo. environment [ " PATH " ] !. split ( separator: pathSeparator)
416
+ for path in paths {
417
+ let url = URL ( fileURLWithPath: String ( path) ) . appendingPathComponent ( executable)
418
+ if FileManager . default. isExecutableFile ( atPath: url. path) {
419
+ return url
420
+ }
421
+ }
422
+ throw PackageToJSError ( " Executable \( executable) not found in PATH " )
423
+ }
424
+
360
425
private struct PackageToJSError : Swift . Error , CustomStringConvertible {
361
426
let description : String
362
427
0 commit comments