Skip to content

Package Init - Generate tests for .executable, .tool #8995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 28 additions & 18 deletions Sources/Workspace/InitPackage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -300,20 +300,44 @@ public final class InitPackage {

"""
if packageType == .executable {
let testTarget: String
if !options.supportedTestingLibraries.isEmpty {
testTarget = """
.testTarget(
name: "\(pkgname)Tests",
dependencies: ["\(pkgname)"]
),
"""
} else {
testTarget = ""
}
param += """
.executableTarget(
name: "\(pkgname)"
),
\(testTarget)
]
"""
} else if packageType == .tool {
let testTarget: String
if !options.supportedTestingLibraries.isEmpty {
testTarget = """
.testTarget(
name: "\(pkgname)Tests",
dependencies: ["\(pkgname)"]
),
"""
} else {
testTarget = ""
}
param += """
.executableTarget(
name: "\(pkgname)",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
]
),
\(testTarget)
]
"""
} else if packageType == .buildToolPlugin {
Expand All @@ -337,22 +361,8 @@ public final class InitPackage {
"""
} else if packageType == .macro {
let testTarget: String
if options.supportedTestingLibraries.contains(.swiftTesting) {
testTarget = """

// A test target used to develop the macro implementation.
.testTarget(
name: "\(pkgname)Tests",
dependencies: [
"\(pkgname)Macros",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
.product(name: "Testing", package: "swift-testing"),
]
),
"""
} else if options.supportedTestingLibraries.contains(.xctest) {
if !options.supportedTestingLibraries.isEmpty {
testTarget = """

// A test target used to develop the macro implementation.
.testTarget(
name: "\(pkgname)Tests",
Expand Down Expand Up @@ -661,7 +671,7 @@ public final class InitPackage {
}

switch packageType {
case .empty, .executable, .tool, .buildToolPlugin, .commandPlugin: return
case .empty, .buildToolPlugin, .commandPlugin: return
default: break
}
let tests = destinationPath.appending("Tests")
Expand Down Expand Up @@ -874,8 +884,8 @@ public final class InitPackage {

let testClassFile = try AbsolutePath(validating: "\(moduleName)Tests.swift", relativeTo: testModule)
switch packageType {
case .empty, .buildToolPlugin, .commandPlugin, .executable, .tool: break
case .library:
case .empty, .buildToolPlugin, .commandPlugin: break
case .library, .executable, .tool:
try writeLibraryTestsFile(testClassFile)
case .macro:
try writeMacroTestsFile(testClassFile)
Expand Down
56 changes: 55 additions & 1 deletion Sources/_InternalTestSupport/SwiftTesting+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,67 @@

import Basics
import Testing
import TSCTestSupport

// MARK: File Helpers

/// Verifies that a file exists at the specified path.
///
/// - Parameters:
/// - path: The absolute path to check for file existence.
/// - sourceLocation: The source location where the expectation is made.
public func expectFileExists(
at path: AbsolutePath,
sourceLocation: SourceLocation = #_sourceLocation,
) {

#expect(
localFileSystem.exists(path),
"Files '\(path)' does not exist.",
sourceLocation: sourceLocation,
)
}

/// Verifies that no file or directory exists at the specified path.
///
/// - Parameters:
/// - path: The absolute path to check for non-existence.
/// - sourceLocation: The source location where the expectation is made.
public func expectNoSuchPath(
_ path: AbsolutePath,
sourceLocation: SourceLocation = #_sourceLocation
) {
#expect(
!localFileSystem.exists(path),
"Expected no such path '\(path)'",
sourceLocation: sourceLocation
)
}

/// Verifies that a directory exists at the specified path.
///
/// - Parameters:
/// - path: The absolute path to check for directory existence.
/// - sourceLocation: The source location where the expectation is made.
public func expectDirectoryExists(
_ path: AbsolutePath,
sourceLocation: SourceLocation = #_sourceLocation
) {
#expect(
localFileSystem.isDirectory(path),
"Expected directory at '\(path)'",
sourceLocation: sourceLocation
)
}

// MARK: Error Helpers

/// Verifies that an expression throws a `CommandExecutionError`.
///
/// - Parameters:
/// - expression: The expression to evaluate.
/// - message: An optional description of the failure.
/// - sourceLocation: The source location where the expectation is made.
/// - errorHandler: A closure that's called with the error if the expression throws.
public func expectThrowsCommandExecutionError<T>(
_ expression: @autoclosure () async throws -> T,
_ message: @autoclosure () -> Comment = "",
Expand All @@ -42,6 +89,12 @@ public func expectThrowsCommandExecutionError<T>(
}

/// An `async`-friendly replacement for `XCTAssertThrowsError`.
///
/// - Parameters:
/// - expression: The expression to evaluate.
/// - message: An optional description of the failure.
/// - sourceLocation: The source location where the expectation is made.
/// - errorHandler: A closure that's called with the error if the expression throws.
public func expectAsyncThrowsError<T>(
_ expression: @autoclosure () async throws -> T,
_ message: @autoclosure () -> Comment? = nil,
Expand All @@ -55,3 +108,4 @@ public func expectAsyncThrowsError<T>(
errorHandler(error)
}
}

34 changes: 15 additions & 19 deletions Tests/IntegrationTests/BasicTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,27 +160,23 @@ private struct BasicTests {
// Create a new package with an executable target.
let packagePath = tempDir.appending(component: "Project")
try localFileSystem.createDirectory(packagePath)
await withKnownIssue("error: no tests found; create a target in the 'Tests' directory") {
try await executeSwiftPackage(
packagePath,
extraArgs: ["init", "--type", "executable"],
buildSystem: .native,
)
let packageOutput = try await executeSwiftTest(
packagePath,
extraArgs: ["--vv"],
buildSystem: .native,
)
try await executeSwiftPackage(
packagePath,
extraArgs: ["init", "--type", "executable"],
buildSystem: .native,
)
let packageOutput = try await executeSwiftTest(
packagePath,
extraArgs: ["--vv"],
buildSystem: .native,
)

// Check the test log.
let compilingRegex = try Regex("Compiling .*ProjectTests.*")
#expect(packageOutput.stdout.contains(compilingRegex), "stdout: '\(packageOutput.stdout)'\n stderr:'\(packageOutput.stderr)'")
#expect(packageOutput.stdout.contains("Executed 1 test"), "stdout: '\(packageOutput.stdout)'\n stderr:'\(packageOutput.stderr)'")
// Check the test log.
#expect(packageOutput.stdout.contains("Test run with 1 test"), "stdout: '\(packageOutput.stdout)'\n stderr:'\(packageOutput.stderr)'")

// Check there were no compile errors or warnings.
#expect(packageOutput.stdout.contains("error") == false)
#expect(packageOutput.stdout.contains("warning") == false)
}
// Check there were no compile errors or warnings.
#expect(packageOutput.stdout.contains("error") == false)
#expect(packageOutput.stdout.contains("warning") == false)
}
}

Expand Down
Loading