Skip to content

Distinguish when an error is part of a recorded issue in an exit test. #718

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

Merged
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
2 changes: 2 additions & 0 deletions Sources/Testing/ABI/v0/Encoded/ABIv0.EncodedBacktrace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ extension ABIv0 {
/// This type is not part of the public interface of the testing library. It
/// assists in converting values to JSON; clients that consume this JSON are
/// expected to write their own decoders.
///
/// - Warning: Backtraces are not yet part of the JSON schema.
struct EncodedBacktrace: Sendable {
/// The frames in the backtrace.
var symbolicatedAddresses: [Backtrace.SymbolicatedAddress]
Expand Down
67 changes: 67 additions & 0 deletions Sources/Testing/ABI/v0/Encoded/ABIv0.EncodedError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 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
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

extension ABIv0 {
/// A type implementing the JSON encoding of ``Error`` for the ABI entry point
/// and event stream output.
///
/// This type is not part of the public interface of the testing library. It
/// assists in converting values to JSON; clients that consume this JSON are
/// expected to write their own decoders.
///
/// - Warning: Errors are not yet part of the JSON schema.
struct EncodedError: Sendable {
/// The error's description
var description: String

/// The domain of the error.
var domain: String

/// The code of the error.
var code: Int

// TODO: userInfo (partial) encoding

init(encoding error: some Error, in eventContext: borrowing Event.Context) {
description = String(describingForTest: error)
domain = error._domain
code = error._code
}
}
}

// MARK: - Error

extension ABIv0.EncodedError: Error {
var _domain: String {
domain
}

var _code: Int {
code
}

var _userInfo: AnyObject? {
// TODO: userInfo (partial) encoding
nil
}
}

// MARK: - Codable

extension ABIv0.EncodedError: Codable {}

// MARK: - CustomTestStringConvertible

extension ABIv0.EncodedError: CustomTestStringConvertible {
var testDescription: String {
description
}
}
10 changes: 10 additions & 0 deletions Sources/Testing/ABI/v0/Encoded/ABIv0.EncodedIssue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,24 @@ extension ABIv0 {
var sourceLocation: SourceLocation?

/// The backtrace where this issue occurred, if available.
///
/// - Warning: Backtraces are not yet part of the JSON schema.
var _backtrace: EncodedBacktrace?

/// The error associated with this issue, if applicable.
///
/// - Warning: Errors are not yet part of the JSON schema.
var _error: EncodedError?

init(encoding issue: borrowing Issue, in eventContext: borrowing Event.Context) {
isKnown = issue.isKnown
sourceLocation = issue.sourceLocation
if let backtrace = issue.sourceContext.backtrace {
_backtrace = EncodedBacktrace(encoding: backtrace, in: eventContext)
}
if let error = issue.error {
_error = EncodedError(encoding: error, in: eventContext)
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions Sources/Testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_library(Testing
ABI/v0/ABIv0.Record+Streaming.swift
ABI/v0/ABIv0.swift
ABI/v0/Encoded/ABIv0.EncodedBacktrace.swift
ABI/v0/Encoded/ABIv0.EncodedError.swift
ABI/v0/Encoded/ABIv0.EncodedEvent.swift
ABI/v0/Encoded/ABIv0.EncodedInstant.swift
ABI/v0/Encoded/ABIv0.EncodedIssue.swift
Expand Down
9 changes: 7 additions & 2 deletions Sources/Testing/ExitTests/ExitTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -505,12 +505,17 @@ extension ExitTest {
let comments: [Comment] = event.messages.compactMap { message in
message.symbol == .details ? Comment(rawValue: message.text) : nil
}
let issueKind: Issue.Kind = if let error = issue._error {
.errorCaught(error)
} else {
// TODO: improve fidelity of issue kind reporting (especially those without associated values)
.unconditional
}
let sourceContext = SourceContext(
backtrace: nil, // `issue._backtrace` will have the wrong address space.
sourceLocation: issue.sourceLocation
)
// TODO: improve fidelity of issue kind reporting (especially those without associated values)
var issueCopy = Issue(kind: .unconditional, comments: comments, sourceContext: sourceContext)
var issueCopy = Issue(kind: issueKind, comments: comments, sourceContext: sourceContext)
issueCopy.isKnown = issue.isKnown
issueCopy.record()
}
Expand Down
39 changes: 22 additions & 17 deletions Tests/TestingTests/ExitTestTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ private import _TestingInternals
await Task.yield()
exit(123)
}
await #expect(exitsWith: .failure) {
throw MyError()
}
#if !os(Windows)
await #expect(exitsWith: .signal(SIGKILL)) {
_ = kill(getpid(), SIGKILL)
Expand Down Expand Up @@ -203,22 +200,30 @@ private import _TestingInternals

@Test("Exit test forwards issues") func forwardsIssues() async {
await confirmation("Issue recorded") { issueRecorded in
var configuration = Configuration()
configuration.eventHandler = { event, _ in
if case let .issueRecorded(issue) = event.kind,
case .unconditional = issue.kind,
issue.comments.contains("Something went wrong!") {
issueRecorded()
await confirmation("Error caught") { errorCaught in
var configuration = Configuration()
configuration.eventHandler = { event, _ in
guard case let .issueRecorded(issue) = event.kind else {
return
}
if case .unconditional = issue.kind, issue.comments.contains("Something went wrong!") {
issueRecorded()
} else if issue.error != nil {
errorCaught()
}
}
}
configuration.exitTestHandler = ExitTest.handlerForEntryPoint()
configuration.exitTestHandler = ExitTest.handlerForEntryPoint()

await Test {
await #expect(exitsWith: .success) {
#expect(Bool(false), "Something went wrong!")
exit(0)
}
}.run(configuration: configuration)
await Test {
await #expect(exitsWith: .success) {
#expect(Bool(false), "Something went wrong!")
exit(0)
}
await #expect(exitsWith: .failure) {
Issue.record(MyError())
}
}.run(configuration: configuration)
}
}
}

Expand Down