Changes to project structure to allow for better testability (#191)
* Changed sources folder to separate the CLI from the 'Kit' to allow for testability of each command * Fixed Swift 5.2 Support * Removed Path.swift (for now) * Fixed OpenCombine and other version issues for 5.2 support * Removed Path.swift from test target dependency Co-authored-by: thecb4 <cavelle@tehcb4.io>
This commit is contained in:
parent
dd452fb6f1
commit
5fe8c8edac
|
@ -0,0 +1,127 @@
|
|||
{
|
||||
"entries": {
|
||||
"brew": {
|
||||
"pre-commit": {
|
||||
"version": "2.9.3",
|
||||
"bottle": {
|
||||
"cellar": ":any",
|
||||
"prefix": "/usr/local",
|
||||
"files": {
|
||||
"big_sur": {
|
||||
"url": "https://homebrew.bintray.com/bottles/pre-commit-2.9.3.big_sur.bottle.tar.gz",
|
||||
"sha256": "81f18e83e858feffacc9a9441c836a2abcf5b1a880f09ee99a261f12ac8ceca7"
|
||||
},
|
||||
"catalina": {
|
||||
"url": "https://homebrew.bintray.com/bottles/pre-commit-2.9.3.catalina.bottle.tar.gz",
|
||||
"sha256": "16977b9f715b4330e7975761edddbaad4b891b25e6c8e4c4186ca4e2e99b11e7"
|
||||
},
|
||||
"mojave": {
|
||||
"url": "https://homebrew.bintray.com/bottles/pre-commit-2.9.3.mojave.bottle.tar.gz",
|
||||
"sha256": "82af5cc062aed6375aff5dfc759bac2cbc549ae0709f1a720ec805368944b46e"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"swiftformat": {
|
||||
"version": "0.47.8",
|
||||
"bottle": {
|
||||
"cellar": ":any_skip_relocation",
|
||||
"prefix": "/usr/local",
|
||||
"files": {
|
||||
"big_sur": {
|
||||
"url": "https://homebrew.bintray.com/bottles/swiftformat-0.47.8.big_sur.bottle.tar.gz",
|
||||
"sha256": "47e3aa92792a53413eb0866d3b6709e88c7164184f406c3e49c560e82a2fe589"
|
||||
},
|
||||
"catalina": {
|
||||
"url": "https://homebrew.bintray.com/bottles/swiftformat-0.47.8.catalina.bottle.tar.gz",
|
||||
"sha256": "c268a44da4ed4e7836d2b43f917a1411f3b8f13488ee25c66f4bd8bb2f4d5e05"
|
||||
},
|
||||
"mojave": {
|
||||
"url": "https://homebrew.bintray.com/bottles/swiftformat-0.47.8.mojave.bottle.tar.gz",
|
||||
"sha256": "38abb30c870974c65ee0a3afed11cd5f983ab05a13923061fc67c4b6566df6f8"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"swiftlint": {
|
||||
"version": "0.42.0",
|
||||
"bottle": {
|
||||
"cellar": ":any_skip_relocation",
|
||||
"prefix": "/usr/local",
|
||||
"files": {
|
||||
"big_sur": {
|
||||
"url": "https://homebrew.bintray.com/bottles/swiftlint-0.42.0.big_sur.bottle.tar.gz",
|
||||
"sha256": "1a0540f0ff6cac2da0a51672db7963aef71cc58954c34791e9b01dafd63c5898"
|
||||
},
|
||||
"catalina": {
|
||||
"url": "https://homebrew.bintray.com/bottles/swiftlint-0.42.0.catalina.bottle.tar.gz",
|
||||
"sha256": "e9023ed754eb8cb78a9f2b469a90875ca42a7afffd3e96f8142252e81d889793"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"binaryen": {
|
||||
"version": "98",
|
||||
"bottle": {
|
||||
"cellar": ":any",
|
||||
"prefix": "/usr/local",
|
||||
"files": {
|
||||
"big_sur": {
|
||||
"url": "https://homebrew.bintray.com/bottles/binaryen-98.big_sur.bottle.tar.gz",
|
||||
"sha256": "5d5ac79ec3aabebd8e62433c84bd0d61b0f4ee44d56e4c4f47475276a6428bd8"
|
||||
},
|
||||
"catalina": {
|
||||
"url": "https://homebrew.bintray.com/bottles/binaryen-98.catalina.bottle.tar.gz",
|
||||
"sha256": "260a8deac0c78a9fead982cf6d05fd818330bc4e28cdc73ecd5de6952ded6e8b"
|
||||
},
|
||||
"mojave": {
|
||||
"url": "https://homebrew.bintray.com/bottles/binaryen-98.mojave.bottle.tar.gz",
|
||||
"sha256": "7a68230a62307fbd61638803e942e4d556f99b8e4d6fbbdd3dbaf6997b3b2843"
|
||||
},
|
||||
"high_sierra": {
|
||||
"url": "https://homebrew.bintray.com/bottles/binaryen-98.high_sierra.bottle.tar.gz",
|
||||
"sha256": "4656a9b10a7db143e3619126ea1e0c169d05b06b27c7e75d5f641c86135c7b1f"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"wasmer": {
|
||||
"version": "0.16.2",
|
||||
"bottle": {
|
||||
"cellar": ":any_skip_relocation",
|
||||
"prefix": "/usr/local",
|
||||
"files": {
|
||||
"big_sur": {
|
||||
"url": "https://homebrew.bintray.com/bottles/wasmer-0.16.2.big_sur.bottle.tar.gz",
|
||||
"sha256": "63d91bbfece68628e7bb464cd8d3a90a6dc89344c4889ada157f174db62f05da"
|
||||
},
|
||||
"catalina": {
|
||||
"url": "https://homebrew.bintray.com/bottles/wasmer-0.16.2.catalina.bottle.tar.gz",
|
||||
"sha256": "751b4b059036dbca254eef935bc03240e1fd559465a376a0cff8f5a41dcd3980"
|
||||
},
|
||||
"mojave": {
|
||||
"url": "https://homebrew.bintray.com/bottles/wasmer-0.16.2.mojave.bottle.tar.gz",
|
||||
"sha256": "725d2b857e0954b1e2fd8a01021847e168d5daec33cd76c32f90a0ae12fdf422"
|
||||
},
|
||||
"high_sierra": {
|
||||
"url": "https://homebrew.bintray.com/bottles/wasmer-0.16.2.high_sierra.bottle.tar.gz",
|
||||
"sha256": "42ea898c1ebd9c0ac58bf21117c05df6a4726123590444c19173a01586c80c63"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"macos": {
|
||||
"big_sur": {
|
||||
"HOMEBREW_VERSION": "2.6.2",
|
||||
"HOMEBREW_PREFIX": "/usr/local",
|
||||
"Homebrew/homebrew-core": "386111a04c8f09f72c60548e519a3e27fade9c34",
|
||||
"CLT": "",
|
||||
"Xcode": "12.2",
|
||||
"macOS": "11.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,6 +37,15 @@
|
|||
"version": "0.11.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Path.swift",
|
||||
"repositoryURL": "https://github.com/mxcl/Path.swift.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "f062ed9ce3729fb3ba3f43573136d2a0e0c6d209",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "routing-kit",
|
||||
"repositoryURL": "https://github.com/vapor/routing-kit.git",
|
||||
|
|
|
@ -12,6 +12,14 @@ let openCombineProduct = Target.Dependency.product(
|
|||
let package = Package(
|
||||
name: "carton",
|
||||
platforms: [.macOS(.v10_15)],
|
||||
products: [
|
||||
.library(name: "SwiftToolchain", targets: ["SwiftToolchain"]),
|
||||
.library(name: "CartonHelpers", targets: ["CartonHelpers"]),
|
||||
.library(name: "CartonKit", targets: ["CartonKit"]),
|
||||
.library(name: "CartonCLI", targets: ["CartonCLI"]),
|
||||
.executable(name: "carton", targets: ["Carton"]),
|
||||
.executable(name: "carton-release", targets: ["carton-release"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.2.2"),
|
||||
.package(
|
||||
|
@ -27,13 +35,33 @@ let package = Package(
|
|||
.package(url: "https://github.com/apple/swift-crypto.git", from: "1.1.0"),
|
||||
.package(url: "https://github.com/JohnSundell/Splash.git", from: "0.14.0"),
|
||||
.package(url: "https://github.com/swiftwasm/WasmTransformer", .upToNextMinor(from: "0.0.2")),
|
||||
.package(url: "https://github.com/mxcl/Path.swift.git", .exact("1.0.0")),
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module
|
||||
// or a test suite. Targets can depend on other targets in this package, and on
|
||||
// products in packages which this package depends on.
|
||||
.target(
|
||||
name: "carton",
|
||||
name: "Carton",
|
||||
dependencies: [
|
||||
"CartonCLI",
|
||||
// commented out for now. Will remove once confirmed working
|
||||
// .product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
// .product(name: "AsyncHTTPClient", package: "async-http-client"),
|
||||
// .product(name: "Crypto", package: "swift-crypto"),
|
||||
// .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
|
||||
// .product(name: "Vapor", package: "vapor"),
|
||||
// "CartonHelpers",
|
||||
// openCombineProduct,
|
||||
// "SwiftToolchain",
|
||||
]
|
||||
),
|
||||
.target(
|
||||
name: "CartonCLI",
|
||||
dependencies: ["CartonKit"]
|
||||
),
|
||||
.target(
|
||||
name: "CartonKit",
|
||||
dependencies: [
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
.product(name: "AsyncHTTPClient", package: "async-http-client"),
|
||||
|
@ -78,9 +106,17 @@ let package = Package(
|
|||
.testTarget(
|
||||
name: "CartonTests",
|
||||
dependencies: [
|
||||
"carton",
|
||||
"Carton",
|
||||
"CartonHelpers",
|
||||
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
.product(name: "Path", package: "Path.swift"),
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "CartonCLITests",
|
||||
dependencies: [
|
||||
"CartonCLI",
|
||||
]
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,10 +6,18 @@ import PackageDescription
|
|||
let package = Package(
|
||||
name: "carton",
|
||||
platforms: [.macOS(.v10_15)],
|
||||
products: [
|
||||
.library(name: "SwiftToolchain", targets: ["SwiftToolchain"]),
|
||||
.library(name: "CartonHelpers", targets: ["CartonHelpers"]),
|
||||
.library(name: "CartonKit", targets: ["CartonKit"]),
|
||||
.library(name: "CartonCLI", targets: ["CartonCLI"]),
|
||||
.executable(name: "carton", targets: ["Carton"]),
|
||||
.executable(name: "carton-release", targets: ["carton-release"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.1.1"),
|
||||
.package(
|
||||
url: "https://github.com/apple/swift-argument-parser",
|
||||
url: "https://github.com/apple/swift-argument-parser.git",
|
||||
.upToNextMinor(from: "0.3.0")
|
||||
),
|
||||
.package(
|
||||
|
@ -27,7 +35,26 @@ let package = Package(
|
|||
// or a test suite. Targets can depend on other targets in this package, and on
|
||||
// products in packages which this package depends on.
|
||||
.target(
|
||||
name: "carton",
|
||||
name: "Carton",
|
||||
dependencies: [
|
||||
"CartonCLI",
|
||||
// commented out for now. Will remove once confirmed working
|
||||
// .product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
// .product(name: "AsyncHTTPClient", package: "async-http-client"),
|
||||
// .product(name: "Crypto", package: "swift-crypto"),
|
||||
// .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
|
||||
// .product(name: "Vapor", package: "vapor"),
|
||||
// "CartonHelpers",
|
||||
// openCombineProduct,
|
||||
// "SwiftToolchain",
|
||||
]
|
||||
),
|
||||
.target(
|
||||
name: "CartonCLI",
|
||||
dependencies: ["CartonKit"]
|
||||
),
|
||||
.target(
|
||||
name: "CartonKit",
|
||||
dependencies: [
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
.product(name: "AsyncHTTPClient", package: "async-http-client"),
|
||||
|
@ -71,7 +98,18 @@ let package = Package(
|
|||
),
|
||||
.testTarget(
|
||||
name: "CartonTests",
|
||||
dependencies: ["carton"]
|
||||
dependencies: [
|
||||
"Carton",
|
||||
"CartonHelpers",
|
||||
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "CartonCLITests",
|
||||
dependencies: [
|
||||
"CartonCLI",
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -12,4 +12,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import CartonCLI
|
||||
|
||||
Carton.main()
|
|
@ -15,10 +15,12 @@
|
|||
import ArgumentParser
|
||||
import CartonHelpers
|
||||
|
||||
struct Carton: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct Carton: ParsableCommand {
|
||||
public static let configuration = CommandConfiguration(
|
||||
abstract: "📦 Watcher, bundler, and test runner for your SwiftWasm apps.",
|
||||
version: cartonVersion,
|
||||
subcommands: [Bundle.self, Dev.self, Init.self, SDK.self, Test.self, Package.self]
|
||||
)
|
||||
|
||||
public init() {}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import ArgumentParser
|
||||
import CartonHelpers
|
||||
import CartonKit
|
||||
import Crypto
|
||||
import SwiftToolchain
|
||||
import TSCBasic
|
|
@ -20,6 +20,7 @@ import Combine
|
|||
#else
|
||||
import OpenCombine
|
||||
#endif
|
||||
import CartonKit
|
||||
import SwiftToolchain
|
||||
import TSCBasic
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import ArgumentParser
|
||||
import CartonHelpers
|
||||
import CartonKit
|
||||
import Foundation
|
||||
import SwiftToolchain
|
||||
import TSCBasic
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import ArgumentParser
|
||||
import CartonHelpers
|
||||
import CartonKit
|
||||
import TSCBasic
|
||||
|
||||
struct ListTemplates: ParsableCommand {
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import ArgumentParser
|
||||
import CartonHelpers
|
||||
import CartonKit
|
||||
import SwiftToolchain
|
||||
import TSCBasic
|
||||
|
|
@ -19,6 +19,7 @@ import Combine
|
|||
import OpenCombine
|
||||
#endif
|
||||
import CartonHelpers
|
||||
import CartonKit
|
||||
import SwiftToolchain
|
||||
import TSCBasic
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
import Crypto
|
||||
import TSCBasic
|
||||
|
||||
extension ByteString {
|
||||
public extension ByteString {
|
||||
var hexSHA256: String {
|
||||
ByteString(SHA256.hash(data: contents)).hexadecimalRepresentation
|
||||
}
|
|
@ -33,15 +33,20 @@ private let verifyHash = Equality<ByteString, String> {
|
|||
"""
|
||||
}
|
||||
|
||||
enum EntrypointError: Error {
|
||||
public enum EntrypointError: Error {
|
||||
case downloadFailed(url: String)
|
||||
}
|
||||
|
||||
struct Entrypoint {
|
||||
public struct Entrypoint {
|
||||
let fileName: String
|
||||
let sha256: ByteString
|
||||
|
||||
func paths(
|
||||
public init(fileName: String, sha256: ByteString) {
|
||||
self.fileName = fileName
|
||||
self.sha256 = sha256
|
||||
}
|
||||
|
||||
public func paths(
|
||||
on fileSystem: FileSystem
|
||||
// swiftlint:disable:next large_tuple
|
||||
) -> (cartonDir: AbsolutePath, staticDir: AbsolutePath, filePath: AbsolutePath) {
|
||||
|
@ -50,7 +55,7 @@ struct Entrypoint {
|
|||
return (cartonDir, staticDir, staticDir.appending(component: fileName))
|
||||
}
|
||||
|
||||
func check(on fileSystem: FileSystem, _ terminal: InteractiveWriter) throws {
|
||||
public func check(on fileSystem: FileSystem, _ terminal: InteractiveWriter) throws {
|
||||
let (cartonDir, staticDir, filePath) = paths(on: fileSystem)
|
||||
|
||||
// If hash check fails, download the `static.zip` archive and unpack it
|
|
@ -14,8 +14,14 @@
|
|||
|
||||
import TSCBasic
|
||||
|
||||
struct Project {
|
||||
public struct Project {
|
||||
let name: String
|
||||
let path: AbsolutePath
|
||||
let inPlace: Bool
|
||||
|
||||
public init(name: String, path: AbsolutePath, inPlace: Bool) {
|
||||
self.name = name
|
||||
self.path = path
|
||||
self.inPlace = inPlace
|
||||
}
|
||||
}
|
|
@ -16,11 +16,11 @@ import CartonHelpers
|
|||
import SwiftToolchain
|
||||
import TSCBasic
|
||||
|
||||
enum Templates: String, CaseIterable {
|
||||
public enum Templates: String, CaseIterable {
|
||||
case basic
|
||||
case tokamak
|
||||
|
||||
var template: Template.Type {
|
||||
public var template: Template.Type {
|
||||
switch self {
|
||||
case .basic: return Basic.self
|
||||
case .tokamak: return Tokamak.self
|
||||
|
@ -28,7 +28,7 @@ enum Templates: String, CaseIterable {
|
|||
}
|
||||
}
|
||||
|
||||
protocol Template {
|
||||
public protocol Template {
|
||||
static var description: String { get }
|
||||
static func create(
|
||||
on fileSystem: FileSystem,
|
|
@ -21,7 +21,7 @@ enum HTMLError: String, Error {
|
|||
"""
|
||||
}
|
||||
|
||||
struct HTML {
|
||||
public struct HTML {
|
||||
let value: String
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,9 @@ extension HTML: ResponseEncodable {
|
|||
))
|
||||
}
|
||||
|
||||
static func readCustomIndexPage(at path: String?, on fileSystem: FileSystem) throws -> String? {
|
||||
public static func readCustomIndexPage(at path: String?,
|
||||
on fileSystem: FileSystem) throws -> String?
|
||||
{
|
||||
if let customIndexPage = path {
|
||||
let content = try localFileSystem.readFileContents(customIndexPage.isAbsolutePath ?
|
||||
AbsolutePath(customIndexPage) :
|
||||
|
@ -50,7 +52,7 @@ extension HTML: ResponseEncodable {
|
|||
}
|
||||
}
|
||||
|
||||
static func indexPage(customContent: String?, entrypointName: String) -> String {
|
||||
public static func indexPage(customContent: String?, entrypointName: String) -> String {
|
||||
let scriptTag = #"<script type="text/javascript" src="\#(entrypointName)"></script>"#
|
||||
if let customContent = customContent {
|
||||
return customContent.replacingOccurrences(
|
|
@ -66,7 +66,7 @@ extension WebSocket: Hashable {
|
|||
}
|
||||
}
|
||||
|
||||
final class Server {
|
||||
public final class Server {
|
||||
private let decoder = JSONDecoder()
|
||||
private var connections = Set<WebSocket>()
|
||||
private var subscriptions = [AnyCancellable]()
|
||||
|
@ -75,7 +75,7 @@ final class Server {
|
|||
private let localURL: String
|
||||
private let skipAutoOpen: Bool
|
||||
|
||||
struct Configuration {
|
||||
public struct Configuration {
|
||||
let builder: Builder?
|
||||
let mainWasmPath: AbsolutePath
|
||||
let verbose: Bool
|
||||
|
@ -85,9 +85,31 @@ final class Server {
|
|||
let package: SwiftToolchain.Package
|
||||
let product: Product?
|
||||
let entrypoint: Entrypoint
|
||||
|
||||
public init(
|
||||
builder: Builder?,
|
||||
mainWasmPath: AbsolutePath,
|
||||
verbose: Bool,
|
||||
skipAutoOpen: Bool,
|
||||
port: Int,
|
||||
customIndexContent: String?,
|
||||
package: SwiftToolchain.Package,
|
||||
product: Product?,
|
||||
entrypoint: Entrypoint
|
||||
) {
|
||||
self.builder = builder
|
||||
self.mainWasmPath = mainWasmPath
|
||||
self.verbose = verbose
|
||||
self.skipAutoOpen = skipAutoOpen
|
||||
self.port = port
|
||||
self.customIndexContent = customIndexContent
|
||||
self.package = package
|
||||
self.product = product
|
||||
self.entrypoint = entrypoint
|
||||
}
|
||||
}
|
||||
|
||||
init(
|
||||
public init(
|
||||
with configuration: Configuration,
|
||||
_ terminal: InteractiveWriter
|
||||
) throws {
|
||||
|
@ -151,7 +173,7 @@ final class Server {
|
|||
}
|
||||
|
||||
/// Blocking function that starts the HTTP server
|
||||
func run() throws {
|
||||
public func run() throws {
|
||||
defer { app.shutdown() }
|
||||
try app.run()
|
||||
for conn in connections {
|
|
@ -1,21 +1,21 @@
|
|||
import TSCBasic
|
||||
|
||||
let devEntrypointSHA256 = ByteString([
|
||||
public let devEntrypointSHA256 = ByteString([
|
||||
0xBF, 0x7E, 0x84, 0x1D, 0x61, 0x40, 0x88, 0x83, 0xBC, 0x4C, 0x80, 0x24, 0x35, 0xEB, 0xED, 0x0D,
|
||||
0xF1, 0x5D, 0x22, 0x80, 0xC8, 0x70, 0x3D, 0xF0, 0xC6, 0x0C, 0x8A, 0x6F, 0x55, 0xD4, 0x8A, 0xF8,
|
||||
])
|
||||
|
||||
let bundleEntrypointSHA256 = ByteString([
|
||||
public let bundleEntrypointSHA256 = ByteString([
|
||||
0x2B, 0xD2, 0xB5, 0x0C, 0x08, 0xFB, 0x5A, 0x8D, 0x55, 0xC4, 0x4B, 0x3A, 0x51, 0x63, 0x80, 0x1F,
|
||||
0xB9, 0x15, 0x50, 0xD2, 0xB6, 0x12, 0xC3, 0xDE, 0x3A, 0x7D, 0xEB, 0xB0, 0x1F, 0x40, 0x06, 0x3F,
|
||||
])
|
||||
|
||||
let testEntrypointSHA256 = ByteString([
|
||||
public let testEntrypointSHA256 = ByteString([
|
||||
0x51, 0x8C, 0xAF, 0x03, 0x7C, 0x12, 0x0A, 0xF4, 0xBC, 0xD1, 0xA0, 0x1B, 0xA2, 0xA8, 0x2B, 0x22,
|
||||
0xDE, 0x93, 0x4D, 0x68, 0x6C, 0x09, 0xAC, 0x20, 0x58, 0x24, 0x31, 0x0F, 0xD6, 0xAA, 0x99, 0x8A,
|
||||
])
|
||||
|
||||
let staticArchiveHash = ByteString([
|
||||
public let staticArchiveHash = ByteString([
|
||||
0x49, 0x84, 0x3D, 0x18, 0xC5, 0x43, 0x32, 0xFE, 0x80, 0x0B, 0x5D, 0xBE, 0xF3, 0x6A, 0x52, 0x9F,
|
||||
0xD8, 0x27, 0x8C, 0xB6, 0x39, 0xAB, 0xAB, 0x6E, 0xE6, 0xCB, 0x82, 0x36, 0x21, 0x09, 0x9B, 0x50,
|
||||
])
|
|
@ -0,0 +1,247 @@
|
|||
//===----------------------------------------------------------*- swift -*-===//
|
||||
//
|
||||
// This source file is part of the Swift Argument Parser open source project
|
||||
//
|
||||
// Copyright (c) 2020 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import ArgumentParser
|
||||
import XCTest
|
||||
|
||||
// extensions to the ParsableArguments protocol to facilitate XCTestExpectation support
|
||||
public protocol TestableParsableArguments: ParsableArguments {
|
||||
var didValidateExpectation: XCTestExpectation { get }
|
||||
}
|
||||
|
||||
public extension TestableParsableArguments {
|
||||
mutating func validate() throws {
|
||||
didValidateExpectation.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
// extensions to the ParsableCommand protocol to facilitate XCTestExpectation support
|
||||
public protocol TestableParsableCommand: ParsableCommand, TestableParsableArguments {
|
||||
var didRunExpectation: XCTestExpectation { get }
|
||||
}
|
||||
|
||||
public extension TestableParsableCommand {
|
||||
mutating func run() throws {
|
||||
didRunExpectation.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
public extension XCTestExpectation {
|
||||
convenience init(singleExpectation description: String) {
|
||||
self.init(description: description)
|
||||
expectedFulfillmentCount = 1
|
||||
assertForOverFulfill = true
|
||||
}
|
||||
}
|
||||
|
||||
public func AssertResultFailure<T, U: Error>(
|
||||
_ expression: @autoclosure () -> Result<T, U>,
|
||||
_ message: @autoclosure () -> String = "",
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) {
|
||||
switch expression() {
|
||||
case .success:
|
||||
let msg = message()
|
||||
XCTFail(msg.isEmpty ? "Incorrectly succeeded" : msg, file: file, line: line)
|
||||
case .failure:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func AssertErrorMessage<A>(
|
||||
_ type: A.Type,
|
||||
_ arguments: [String],
|
||||
_ errorMessage: String,
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) where A: ParsableArguments {
|
||||
do {
|
||||
_ = try A.parse(arguments)
|
||||
XCTFail("Parsing should have failed.", file: file, line: line)
|
||||
} catch {
|
||||
// We expect to hit this path, i.e. getting an error:
|
||||
XCTAssertEqual(A.message(for: error), errorMessage, file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
public func AssertFullErrorMessage<A>(
|
||||
_ type: A.Type,
|
||||
_ arguments: [String],
|
||||
_ errorMessage: String,
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) where A: ParsableArguments {
|
||||
do {
|
||||
_ = try A.parse(arguments)
|
||||
XCTFail("Parsing should have failed.", file: file, line: line)
|
||||
} catch {
|
||||
// We expect to hit this path, i.e. getting an error:
|
||||
XCTAssertEqual(A.fullMessage(for: error), errorMessage, file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
public func AssertParse<A>(
|
||||
_ type: A.Type,
|
||||
_ arguments: [String],
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line,
|
||||
closure: (A) throws -> ()
|
||||
) where A: ParsableArguments {
|
||||
do {
|
||||
let parsed = try type.parse(arguments)
|
||||
try closure(parsed)
|
||||
} catch {
|
||||
let message = type.message(for: error)
|
||||
XCTFail("\"\(message)\" — \(error)", file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
public func AssertParseCommand<A: ParsableCommand>(
|
||||
_ rootCommand: ParsableCommand.Type,
|
||||
_ type: A.Type,
|
||||
_ arguments: [String],
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line,
|
||||
closure: (A) throws -> ()
|
||||
) {
|
||||
do {
|
||||
let command = try rootCommand.parseAsRoot(arguments)
|
||||
guard let aCommand = command as? A else {
|
||||
XCTFail("Command is of unexpected type: \(command)", file: file, line: line)
|
||||
return
|
||||
}
|
||||
try closure(aCommand)
|
||||
} catch {
|
||||
let message = rootCommand.message(for: error)
|
||||
XCTFail("\"\(message)\" — \(error)", file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
public func AssertEqualStringsIgnoringTrailingWhitespace(
|
||||
_ string1: String,
|
||||
_ string2: String,
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) {
|
||||
let lines1 = string1.split(separator: "\n", omittingEmptySubsequences: false)
|
||||
let lines2 = string2.split(separator: "\n", omittingEmptySubsequences: false)
|
||||
|
||||
XCTAssertEqual(
|
||||
lines1.count,
|
||||
lines2.count,
|
||||
"Strings have different numbers of lines.",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
for (line1, line2) in zip(lines1, lines2) {
|
||||
XCTAssertEqual(line1.trimmed(), line2.trimmed(), file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
public func AssertHelp<T: ParsableArguments>(
|
||||
for _: T.Type, equals expected: String,
|
||||
file: StaticString = #file, line: UInt = #line
|
||||
) {
|
||||
do {
|
||||
_ = try T.parse(["-h"])
|
||||
XCTFail(file: file, line: line)
|
||||
} catch {
|
||||
let helpString = T.fullMessage(for: error)
|
||||
AssertEqualStringsIgnoringTrailingWhitespace(
|
||||
helpString, expected, file: file, line: line
|
||||
)
|
||||
}
|
||||
|
||||
let helpString = T.helpMessage()
|
||||
AssertEqualStringsIgnoringTrailingWhitespace(
|
||||
helpString, expected, file: file, line: line
|
||||
)
|
||||
}
|
||||
|
||||
public func AssertHelp<T: ParsableCommand, U: ParsableCommand>(
|
||||
for _: T.Type, root _: U.Type, equals expected: String,
|
||||
file: StaticString = #file, line: UInt = #line
|
||||
) {
|
||||
let helpString = U.helpMessage(for: T.self)
|
||||
AssertEqualStringsIgnoringTrailingWhitespace(
|
||||
helpString, expected, file: file, line: line
|
||||
)
|
||||
}
|
||||
|
||||
public extension XCTest {
|
||||
var debugURL: URL {
|
||||
let bundleURL = Bundle(for: type(of: self)).bundleURL
|
||||
return bundleURL.lastPathComponent.hasSuffix("xctest")
|
||||
? bundleURL.deletingLastPathComponent()
|
||||
: bundleURL
|
||||
}
|
||||
|
||||
func AssertExecuteCommand(
|
||||
command: String,
|
||||
expected: String? = nil,
|
||||
exitCode: ExitCode = .success,
|
||||
file: StaticString = #file, line: UInt = #line
|
||||
) {
|
||||
let splitCommand = command.split(separator: " ")
|
||||
let arguments = splitCommand.dropFirst().map(String.init)
|
||||
|
||||
let commandName = String(splitCommand.first!)
|
||||
let commandURL = debugURL.appendingPathComponent(commandName)
|
||||
guard (try? commandURL.checkResourceIsReachable()) ?? false else {
|
||||
XCTFail("No executable at '\(commandURL.standardizedFileURL.path)'.",
|
||||
file: file, line: line)
|
||||
return
|
||||
}
|
||||
|
||||
let process = Process()
|
||||
if #available(macOS 10.13, *) {
|
||||
process.executableURL = commandURL
|
||||
} else {
|
||||
process.launchPath = commandURL.path
|
||||
}
|
||||
process.arguments = arguments
|
||||
|
||||
let output = Pipe()
|
||||
process.standardOutput = output
|
||||
let error = Pipe()
|
||||
process.standardError = error
|
||||
|
||||
if #available(macOS 10.13, *) {
|
||||
guard (try? process.run()) != nil else {
|
||||
XCTFail("Couldn't run command process.", file: file, line: line)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
process.launch()
|
||||
}
|
||||
process.waitUntilExit()
|
||||
|
||||
let outputData = output.fileHandleForReading.readDataToEndOfFile()
|
||||
let outputActual = String(data: outputData, encoding: .utf8)!
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
let errorData = error.fileHandleForReading.readDataToEndOfFile()
|
||||
let errorActual = String(data: errorData, encoding: .utf8)!
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if let expected = expected {
|
||||
AssertEqualStringsIgnoringTrailingWhitespace(
|
||||
expected,
|
||||
errorActual + outputActual,
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
XCTAssertEqual(process.terminationStatus, exitCode.rawValue, file: file, line: line)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2020 Carton contributors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Created by Cavelle Benjamin on Dec/20/20.
|
||||
//
|
||||
|
||||
@testable import CartonCLI
|
||||
import XCTest
|
||||
|
||||
final class DevCommandTests: XCTestCase {
|
||||
func testDefaultArgumentParsing() throws {
|
||||
// given
|
||||
let arguments: [String] = []
|
||||
|
||||
// when
|
||||
|
||||
AssertParse(Dev.self, arguments) { command in
|
||||
// then
|
||||
XCTAssertNotNil(command)
|
||||
}
|
||||
}
|
||||
|
||||
func testHelpString() throws {
|
||||
// given
|
||||
let expectation =
|
||||
"""
|
||||
OVERVIEW: Watch the current directory, host the app, rebuild on change.
|
||||
|
||||
USAGE: carton dev [--product <product>] [--destination <destination>] [--custom-index-page <custom-index-page>] [--release] [--verbose] [--port <port>] [--skip-auto-open]
|
||||
|
||||
OPTIONS:
|
||||
--product <product> Specify name of an executable product in development.
|
||||
--destination <destination>
|
||||
This option has no effect and will be removed in a
|
||||
future version of `carton`
|
||||
--custom-index-page <custom-index-page>
|
||||
Specify a path to a custom `index.html` file to be
|
||||
used for your app.
|
||||
--release When specified, build in the release mode.
|
||||
-v, --verbose Don't clear terminal window after files change.
|
||||
-p, --port <port> Set the HTTP port the development server will run on.
|
||||
(default: 8080)
|
||||
--skip-auto-open Skip automatically opening app in system browser.
|
||||
--version Show the version.
|
||||
-h, --help Show help information.
|
||||
"""
|
||||
// when
|
||||
// then
|
||||
|
||||
AssertExecuteCommand(command: "carton dev -h", expected: expectation)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
//===----------------------------------------------------------*- swift -*-===//
|
||||
//
|
||||
// This source file is part of the Swift Argument Parser open source project
|
||||
//
|
||||
// Copyright (c) 2020 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
extension Substring {
|
||||
func trimmed() -> Substring {
|
||||
guard let i = lastIndex(where: { $0 != " " }) else {
|
||||
return ""
|
||||
}
|
||||
return self[...i]
|
||||
}
|
||||
}
|
||||
|
||||
public extension String {
|
||||
func trimmingLines() -> String {
|
||||
split(separator: "\n", omittingEmptySubsequences: false)
|
||||
.map { $0.trimmed() }
|
||||
.joined(separator: "\n")
|
||||
}
|
||||
}
|
|
@ -58,6 +58,34 @@ final class CartonTests: XCTestCase {
|
|||
XCTAssertEqual(output?.trimmingCharacters(in: .whitespacesAndNewlines), "0.9.1")
|
||||
}
|
||||
|
||||
// func testDev() throws {
|
||||
// // This is an example of a functional test case.
|
||||
// // Use XCTAssert and related functions to verify your tests produce the correct
|
||||
// // results.
|
||||
//
|
||||
// // Some of the APIs that we use below are available in macOS 10.13 and above.
|
||||
// guard #available(macOS 10.13, *) else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// let fooBinary = productsDirectory.appendingPathComponent("carton")
|
||||
//
|
||||
// let process = Process()
|
||||
// process.executableURL = fooBinary
|
||||
//
|
||||
// let pipe = Pipe()
|
||||
// process.standardOutput = pipe
|
||||
//
|
||||
// process.arguments = ["dev"]
|
||||
// try process.run()
|
||||
// process.waitUntilExit()
|
||||
//
|
||||
// let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||
// let output = String(data: data, encoding: .utf8)
|
||||
//
|
||||
// XCTAssertEqual(output?.trimmingCharacters(in: .whitespacesAndNewlines), "0.9.1")
|
||||
// }
|
||||
|
||||
final class TestOutputStream: OutputByteStream {
|
||||
var bytes: [UInt8] = []
|
||||
var currentOutput: String {
|
||||
|
|
Loading…
Reference in New Issue