|
| 1 | +import Foundation |
| 2 | +import Testing |
| 3 | + |
| 4 | +@testable import PackageToJS |
| 5 | + |
| 6 | +extension Trait where Self == ConditionTrait { |
| 7 | + static var requireSwiftSDK: ConditionTrait { |
| 8 | + .enabled( |
| 9 | + if: ProcessInfo.processInfo.environment["SWIFT_SDK_ID"] != nil |
| 10 | + && ProcessInfo.processInfo.environment["SWIFT_PATH"] != nil, |
| 11 | + "Requires SWIFT_SDK_ID and SWIFT_PATH environment variables" |
| 12 | + ) |
| 13 | + } |
| 14 | + |
| 15 | + static func requireSwiftSDK(triple: String) -> ConditionTrait { |
| 16 | + .enabled( |
| 17 | + if: ProcessInfo.processInfo.environment["SWIFT_SDK_ID"] != nil |
| 18 | + && ProcessInfo.processInfo.environment["SWIFT_PATH"] != nil |
| 19 | + && ProcessInfo.processInfo.environment["SWIFT_SDK_ID"]!.hasSuffix(triple), |
| 20 | + "Requires SWIFT_SDK_ID and SWIFT_PATH environment variables" |
| 21 | + ) |
| 22 | + } |
| 23 | + |
| 24 | + static var requireEmbeddedSwift: ConditionTrait { |
| 25 | + // Check if $SWIFT_PATH/../lib/swift/embedded/wasm32-unknown-none-wasm/ exists |
| 26 | + return .enabled( |
| 27 | + if: { |
| 28 | + guard let swiftPath = ProcessInfo.processInfo.environment["SWIFT_PATH"] else { |
| 29 | + return false |
| 30 | + } |
| 31 | + let embeddedPath = URL(fileURLWithPath: swiftPath).deletingLastPathComponent() |
| 32 | + .appending(path: "lib/swift/embedded/wasm32-unknown-none-wasm") |
| 33 | + return FileManager.default.fileExists(atPath: embeddedPath.path) |
| 34 | + }(), |
| 35 | + "Requires embedded Swift SDK under $SWIFT_PATH/../lib/swift/embedded" |
| 36 | + ) |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +@Suite struct ExampleTests { |
| 41 | + static func getSwiftSDKID() -> String? { |
| 42 | + ProcessInfo.processInfo.environment["SWIFT_SDK_ID"] |
| 43 | + } |
| 44 | + |
| 45 | + static let repoPath = URL(fileURLWithPath: #filePath) |
| 46 | + .deletingLastPathComponent() |
| 47 | + .deletingLastPathComponent() |
| 48 | + .deletingLastPathComponent() |
| 49 | + .deletingLastPathComponent() |
| 50 | + |
| 51 | + static func copyRepository(to destination: URL) throws { |
| 52 | + try FileManager.default.createDirectory( |
| 53 | + atPath: destination.path, withIntermediateDirectories: true, attributes: nil) |
| 54 | + let ignore = [ |
| 55 | + ".git", |
| 56 | + ".vscode", |
| 57 | + ".build", |
| 58 | + "node_modules", |
| 59 | + ] |
| 60 | + |
| 61 | + let enumerator = FileManager.default.enumerator(atPath: repoPath.path)! |
| 62 | + while let file = enumerator.nextObject() as? String { |
| 63 | + let sourcePath = repoPath.appending(path: file) |
| 64 | + let destinationPath = destination.appending(path: file) |
| 65 | + if ignore.contains(where: { file.hasSuffix($0) }) { |
| 66 | + enumerator.skipDescendants() |
| 67 | + continue |
| 68 | + } |
| 69 | + // Skip directories |
| 70 | + var isDirectory: ObjCBool = false |
| 71 | + if FileManager.default.fileExists(atPath: sourcePath.path, isDirectory: &isDirectory) { |
| 72 | + if isDirectory.boolValue { |
| 73 | + continue |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + do { |
| 78 | + try FileManager.default.createDirectory( |
| 79 | + at: destinationPath.deletingLastPathComponent(), |
| 80 | + withIntermediateDirectories: true, attributes: nil) |
| 81 | + try FileManager.default.copyItem(at: sourcePath, to: destinationPath) |
| 82 | + } catch { |
| 83 | + print("Failed to copy \(sourcePath) to \(destinationPath): \(error)") |
| 84 | + throw error |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + typealias RunSwift = (_ args: [String], _ env: [String: String]) throws -> Void |
| 90 | + |
| 91 | + func withPackage(at path: String, body: (URL, _ runSwift: RunSwift) throws -> Void) throws { |
| 92 | + try withTemporaryDirectory { tempDir, retain in |
| 93 | + let destination = tempDir.appending(path: Self.repoPath.lastPathComponent) |
| 94 | + try Self.copyRepository(to: destination) |
| 95 | + try body(destination.appending(path: path)) { args, env in |
| 96 | + let process = Process() |
| 97 | + process.executableURL = URL( |
| 98 | + fileURLWithPath: "swift", |
| 99 | + relativeTo: URL( |
| 100 | + fileURLWithPath: ProcessInfo.processInfo.environment["SWIFT_PATH"]!)) |
| 101 | + process.arguments = args |
| 102 | + process.currentDirectoryURL = destination.appending(path: path) |
| 103 | + process.environment = ProcessInfo.processInfo.environment.merging(env) { _, new in |
| 104 | + new |
| 105 | + } |
| 106 | + let stdoutPath = tempDir.appending(path: "stdout.txt") |
| 107 | + let stderrPath = tempDir.appending(path: "stderr.txt") |
| 108 | + _ = FileManager.default.createFile(atPath: stdoutPath.path, contents: nil) |
| 109 | + _ = FileManager.default.createFile(atPath: stderrPath.path, contents: nil) |
| 110 | + process.standardOutput = try FileHandle(forWritingTo: stdoutPath) |
| 111 | + process.standardError = try FileHandle(forWritingTo: stderrPath) |
| 112 | + |
| 113 | + try process.run() |
| 114 | + process.waitUntilExit() |
| 115 | + if process.terminationStatus != 0 { |
| 116 | + retain = true |
| 117 | + } |
| 118 | + try #require( |
| 119 | + process.terminationStatus == 0, |
| 120 | + """ |
| 121 | + Swift package should build successfully, check \(destination.appending(path: path).path) for details |
| 122 | + stdout: \(stdoutPath.path) |
| 123 | + stderr: \(stderrPath.path) |
| 124 | +
|
| 125 | + \((try? String(contentsOf: stdoutPath, encoding: .utf8)) ?? "<<stdout is empty>>") |
| 126 | + \((try? String(contentsOf: stderrPath, encoding: .utf8)) ?? "<<stderr is empty>>") |
| 127 | + """ |
| 128 | + ) |
| 129 | + } |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + @Test(.requireSwiftSDK) |
| 134 | + func basic() throws { |
| 135 | + let swiftSDKID = try #require(Self.getSwiftSDKID()) |
| 136 | + try withPackage(at: "Examples/Basic") { packageDir, runSwift in |
| 137 | + try runSwift(["package", "--swift-sdk", swiftSDKID, "js"], [:]) |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + @Test(.requireSwiftSDK) |
| 142 | + func testing() throws { |
| 143 | + let swiftSDKID = try #require(Self.getSwiftSDKID()) |
| 144 | + try withPackage(at: "Examples/Testing") { packageDir, runSwift in |
| 145 | + try runSwift(["package", "--swift-sdk", swiftSDKID, "js", "test"], [:]) |
| 146 | + try runSwift(["package", "--swift-sdk", swiftSDKID, "js", "test", "--environment", "browser"], [:]) |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + @Test(.requireSwiftSDK(triple: "wasm32-unknown-wasip1-threads")) |
| 151 | + func multithreading() throws { |
| 152 | + let swiftSDKID = try #require(Self.getSwiftSDKID()) |
| 153 | + try withPackage(at: "Examples/Multithreading") { packageDir, runSwift in |
| 154 | + try runSwift(["package", "--swift-sdk", swiftSDKID, "js"], [:]) |
| 155 | + } |
| 156 | + } |
| 157 | + |
| 158 | + @Test(.requireSwiftSDK(triple: "wasm32-unknown-wasip1-threads")) |
| 159 | + func offscreenCanvas() throws { |
| 160 | + let swiftSDKID = try #require(Self.getSwiftSDKID()) |
| 161 | + try withPackage(at: "Examples/OffscrenCanvas") { packageDir, runSwift in |
| 162 | + try runSwift(["package", "--swift-sdk", swiftSDKID, "js"], [:]) |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + @Test(.requireSwiftSDK(triple: "wasm32-unknown-wasip1-threads")) |
| 167 | + func actorOnWebWorker() throws { |
| 168 | + let swiftSDKID = try #require(Self.getSwiftSDKID()) |
| 169 | + try withPackage(at: "Examples/ActorOnWebWorker") { packageDir, runSwift in |
| 170 | + try runSwift(["package", "--swift-sdk", swiftSDKID, "js"], [:]) |
| 171 | + } |
| 172 | + } |
| 173 | + |
| 174 | + @Test(.requireEmbeddedSwift) func embedded() throws { |
| 175 | + try withPackage(at: "Examples/Embedded") { packageDir, runSwift in |
| 176 | + try runSwift( |
| 177 | + ["package", "--triple", "wasm32-unknown-none-wasm", "-c", "release", "js"], |
| 178 | + [ |
| 179 | + "JAVASCRIPTKIT_EXPERIMENTAL_EMBEDDED_WASM": "true" |
| 180 | + ] |
| 181 | + ) |
| 182 | + } |
| 183 | + } |
| 184 | +} |
0 commit comments