Skip to content
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

Add a basic implementation of XCTIssue. #466

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ add_library(XCTest
Sources/XCTest/Public/XCTestObservationCenter.swift
Sources/XCTest/Public/XCTestCase+Performance.swift
Sources/XCTest/Public/XCTAssert.swift
Sources/XCTest/Public/XCTIssue.swift
Sources/XCTest/Public/XCTSkip.swift
Sources/XCTest/Public/Asynchronous/XCTNSNotificationExpectation.swift
Sources/XCTest/Public/Asynchronous/XCTNSPredicateExpectation.swift
Expand Down
84 changes: 84 additions & 0 deletions Sources/XCTest/Public/XCTIssue.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//

/// Encapsulates all data concerning a test failure or other issue.
public struct XCTIssue: Sendable {
/// Types of failures and other issues that can be reported for tests.
public enum IssueType: Int, Sendable {
/// Issue raised by a failed XCTAssert or related API.
case assertionFailure = 0

/// Issue raised by the test throwing an error in Swift. This could also occur if an Objective C test is implemented in the form `- (BOOL)testFoo:(NSError **)outError` and returns NO with a non-nil out error.
case thrownError = 1

/// Code in the test throws and does not catch an exception, Objective C, C++, or other.
case uncaughtException = 2

/// One of the XCTestCase(measure:) family of APIs detected a performance regression.
case performanceRegression = 3

/// One of the framework APIs failed internally. For example, XCUIApplication was unable to launch or terminate an app or XCUIElementQuery was unable to complete a query.
case system = 4

/// Issue raised when XCTExpectFailure is used but no matching issue is recorded.
case unmatchedExpectedFailure = 5
grynspan marked this conversation as resolved.
Show resolved Hide resolved

/// A short human-readable description of this issue type.
fileprivate var stringRepresentation: String {
switch self {
case .assertionFailure:
"Assertion Failure"
case .thrownError:
"Thrown Error"
case .uncaughtException:
"Uncaught Exception"
case .performanceRegression:
"Performance Regression"
case .system:
"System Error"
case .warning:
"Warning"
case .unmatchedExpectedFailure:
"Unmatched Expected Failure"
}
}
}

/// The type of the issue.
public var type: IssueType

/// A concise description of the issue, expected to be free of transient data and suitable for use in test run
/// summaries and for aggregation of results across multiple test runs.
public var compactDescription: String

/// A detailed description of the issue designed to help diagnose the issue. May include transient data such as
/// numbers, object identifiers, timestamps, etc.
public var detailedDescription: String?

/// Error associated with the issue.
public var associatedError: (any Error)?

public init(
type: IssueType,
compactDescription: String,
detailedDescription: String? = nil,
associatedError: (any Error)? = nil
) {
self.type = type
self.compactDescription = compactDescription
self.detailedDescription = detailedDescription
self.associatedError = associatedError
}
}

extension XCTIssue: CustomStringConvertible {
public var description: String {
"\(type.stringRepresentation): \(compactDescription)"
}
}
17 changes: 17 additions & 0 deletions Sources/XCTest/Public/XCTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,23 @@ open class XCTestCase: XCTest {
expected: false)
}

/// Records a failure or other issue in the execution of the test and is used by all test assertions.
///
/// - Parameters:
/// - issue: A value with all details related to the issue.
///
/// Overrides of this method should call `super` unless they wish to suppress the issue.
/// `super` can be invoked with a different `issue` object.
open func record(_ issue: XCTIssue, file: StaticString = #file, line: Int = #line) {
let isExpected = issue.type == .assertionFailure || issue.type == .performanceRegression
recordFailure(
withDescription: issue.compactDescription,
inFile: String(file),
atLine: line,
expected: isExpected
)
}

/// Setup method called before the invocation of any test method in the
/// class.
open class func setUp() {}
Expand Down
15 changes: 15 additions & 0 deletions Tests/Functional/ErrorHandling/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class ErrorHandling: XCTestCase {
("test_shouldReportCorrectTypeOnUnwrapFailure", test_shouldReportCorrectTypeOnUnwrapFailure),
("test_shouldReportCustomFileLineLocation", test_shouldReportCustomFileLineLocation),
("test_shouldReportFailureNotOnMainThread", test_shouldReportFailureNotOnMainThread),
("test_canRecordIssue", test_canRecordIssue),
]
}()

Expand Down Expand Up @@ -290,6 +291,20 @@ class ErrorHandling: XCTestCase {

semaphore.wait()
}

// CHECK: Test Case 'ErrorHandling.test_canRecordIssue' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
// CHECK: .*[/\\]ErrorHandling[/\\]main.swift:[[@LINE+10]]: error: ErrorHandling.test_canRecordIssue : Performance Regression: ABC 123
// CHECK: Test Case 'ErrorHandling.test_canRecordIssue' failed \(\d+\.\d+ seconds\)
func test_canRecordIssue() {
struct MyError: Error {}
let issue = XCTIssue(
type: .performanceRegression,
compactDescription: "ABC 123",
detailedDescription: "DEF 987",
associatedError: MyError()
)
record(issue)
}
}

// CHECK: Test Suite 'ErrorHandling' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
Expand Down
4 changes: 4 additions & 0 deletions XCTest.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
DA9D441B1D920A3500108768 /* XCTestCase+Asynchronous.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9D44161D920A3500108768 /* XCTestCase+Asynchronous.swift */; };
DA9D441C1D920A3500108768 /* XCTestExpectation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9D44171D920A3500108768 /* XCTestExpectation.swift */; };
E1495C80224276A600CDEB7D /* IgnoredErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1495C7F224276A600CDEB7D /* IgnoredErrors.swift */; };
FCF8F7132AD069A80074FC0F /* XCTIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCF8F7122AD069A80074FC0F /* XCTIssue.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -91,6 +92,7 @@
DA9D44171D920A3500108768 /* XCTestExpectation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCTestExpectation.swift; sourceTree = "<group>"; };
E1495C7F224276A600CDEB7D /* IgnoredErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IgnoredErrors.swift; sourceTree = "<group>"; };
EA3E74BB1BF2B6D500635A73 /* build_script.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = build_script.py; sourceTree = "<group>"; };
FCF8F7122AD069A80074FC0F /* XCTIssue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCTIssue.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -152,6 +154,7 @@
DA9D44131D920A3500108768 /* Asynchronous */,
AE2FE0EA1CFE86DB003EF0D7 /* XCAbstractTest.swift */,
AE2FE0ED1CFE86DB003EF0D7 /* XCTAssert.swift */,
FCF8F7122AD069A80074FC0F /* XCTIssue.swift */,
1748FE3623AFD2D80014DB87 /* XCTSkip.swift */,
AE2FE0EE1CFE86DB003EF0D7 /* XCTestCase.swift */,
AE2FE0F01CFE86DB003EF0D7 /* XCTestCase+Performance.swift */,
Expand Down Expand Up @@ -361,6 +364,7 @@
172FF8A72117B74D0059CBC5 /* XCTNSNotificationExpectation.swift in Sources */,
AE2FE11A1CFE86E6003EF0D7 /* WallClockTimeMetric.swift in Sources */,
AE2FE1191CFE86E6003EF0D7 /* TestFiltering.swift in Sources */,
FCF8F7132AD069A80074FC0F /* XCTIssue.swift in Sources */,
AE2FE1031CFE86DB003EF0D7 /* XCTestErrors.swift in Sources */,
AE2FE1091CFE86DB003EF0D7 /* XCTestSuite.swift in Sources */,
AE2FE11C1CFE86E6003EF0D7 /* XCTestCaseSuite.swift in Sources */,
Expand Down