1#if canImport(Testing)2import Testing3#endif45#if false6789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 #else67 public func testCase(_ testCase: XCTestCase, didFailWithDescription description: String, inFile filePath: String?, atLine lineNumber: Int) {68 let issue = TestIssue(description: description, inFile: filePath, atLine: lineNumber)69 let record = TestCaseFailureRecord(testCase: .init(testCase), issue: issue, failureKind: .unexpected)70 write(record: TestEventRecord(caseFailure: record))71 }72 #endif7374 public func testCaseDidFinish(_ testCase: XCTestCase) {75 let record = TestCaseEventRecord(testCase: .init(testCase), event: .finish)76 write(record: TestEventRecord(caseEvent: record))77 }7879 #if canImport(Darwin)80 public func testSuite(_ testSuite: XCTestSuite, didRecord issue: XCTIssue) {81 let record = TestSuiteFailureRecord(suite: .init(testSuite), issue: .init(issue), failureKind: .unexpected)82 write(record: TestEventRecord(suiteFailure: record))83 }8485 public func testSuite(_ testSuite: XCTestSuite, didRecord expectedFailure: XCTExpectedFailure) {86 let record = TestSuiteFailureRecord(suite: .init(testSuite), issue: .init(expectedFailure.issue), failureKind: .expected(failureReason: expectedFailure.failureReason))87 write(record: TestEventRecord(suiteFailure: record))88 }89 #else90 public func testSuite(_ testSuite: XCTestSuite, didFailWithDescription description: String, inFile filePath: String?, atLine lineNumber: Int) {91 let issue = TestIssue(description: description, inFile: filePath, atLine: lineNumber)92 let record = TestSuiteFailureRecord(suite: .init(testSuite), issue: issue, failureKind: .unexpected)93 write(record: TestEventRecord(suiteFailure: record))94 }95 #endif9697 public func testSuiteDidFinish(_ testSuite: XCTestSuite) {98 let record = TestSuiteEventRecord(suite: .init(testSuite), event: .finish)99 write(record: TestEventRecord(suiteEvent: record))100 }101102 public func testBundleDidFinish(_ testBundle: Bundle) {103 let record = TestBundleEventRecord(bundle: .init(testBundle), event: .finish)104 write(record: TestEventRecord(bundleEvent: record))105 }106}107108109110#if canImport(Glibc)111@_exported import Glibc112#elseif canImport(Musl)113@_exported import Musl114#elseif os(Windows)115@_exported import CRT116@_exported import WinSDK117#elseif os(WASI)118@_exported import WASILibc119#elseif canImport(Android)120@_exported import Android121#else122@_exported import Darwin.C123#endif124125import Foundation126127public final class FileLock {128 #if os(Windows)129 private var handle: HANDLE?130 #else131 private var fileDescriptor: CInt?132 #endif133134 private let lockFile: URL135136 public init(at lockFile: URL) {137 self.lockFile = lockFile138 }139140 public func lock() throws {141 #if os(Windows)142 if handle == nil {143 let h: HANDLE = lockFile.path.withCString(encodedAs: UTF16.self, {144 CreateFileW(145 $0,146 UInt32(GENERIC_READ) | UInt32(GENERIC_WRITE),147 UInt32(FILE_SHARE_READ) | UInt32(FILE_SHARE_WRITE),148 nil,149 DWORD(OPEN_ALWAYS),150 DWORD(FILE_ATTRIBUTE_NORMAL),151 nil152 )153 })154 if h == INVALID_HANDLE_VALUE {155 throw FileSystemError(errno: Int32(GetLastError()), lockFile)156 }157 self.handle = h158 }159 var overlapped = OVERLAPPED()160 overlapped.Offset = 0161 overlapped.OffsetHigh = 0162 overlapped.hEvent = nil163 if !LockFileEx(handle, DWORD(LOCKFILE_EXCLUSIVE_LOCK), 0,164 UInt32.max, UInt32.max, &overlapped) {165 throw ProcessLockError.unableToAquireLock(errno: Int32(GetLastError()))166 }167 #elseif os(WASI)168 169 #else170 if fileDescriptor == nil {171 let fd = open(lockFile.path, O_WRONLY | O_CREAT | O_CLOEXEC, 0o666)172 if fd == -1 {173 fatalError("errno: \(errno), lockFile: \(lockFile)")174 }175 self.fileDescriptor = fd176 }177 while true {178 if flock(fileDescriptor!, LOCK_EX) == 0 {179 break180 }181 if errno == EINTR { continue }182 fatalError("unable to acquire lock, errno: \(errno)")183 }184 #endif185 }186187 public func unlock() {188 #if os(Windows)189 var overlapped = OVERLAPPED()190 overlapped.Offset = 0191 overlapped.OffsetHigh = 0192 overlapped.hEvent = nil193 UnlockFileEx(handle, 0, UInt32.max, UInt32.max, &overlapped)194 #elseif os(WASI)195 196 #else197 guard let fd = fileDescriptor else { return }198 flock(fd, LOCK_UN)199 #endif200 }201202 deinit {203 #if os(Windows)204 guard let handle = handle else { return }205 CloseHandle(handle)206 #elseif os(WASI)207 208 #else209 guard let fd = fileDescriptor else { return }210 close(fd)211 #endif212 }213214 public func withLock<T>(_ body: () throws -> T) throws -> T {215 try lock()216 defer { unlock() }217 return try body()218 }219220 public func withLock<T>(_ body: () async throws -> T) async throws -> T {221 try lock()222 defer { unlock() }223 return try await body()224 }225}226227228229struct TestEventRecord: Codable {230 let caseFailure: TestCaseFailureRecord?231 let suiteFailure: TestSuiteFailureRecord?232233 let bundleEvent: TestBundleEventRecord?234 let suiteEvent: TestSuiteEventRecord?235 let caseEvent: TestCaseEventRecord?236237 init(238 caseFailure: TestCaseFailureRecord? = nil,239 suiteFailure: TestSuiteFailureRecord? = nil,240 bundleEvent: TestBundleEventRecord? = nil,241 suiteEvent: TestSuiteEventRecord? = nil,242 caseEvent: TestCaseEventRecord? = nil243 ) {244 self.caseFailure = caseFailure245 self.suiteFailure = suiteFailure246 self.bundleEvent = bundleEvent247 self.suiteEvent = suiteEvent248 self.caseEvent = caseEvent249 }250}251252253254struct TestAttachment: Codable {255 let name: String?256 257 let uniformTypeIdentifier: String258 let payload: Data?259}260261struct TestBundleEventRecord: Codable {262 let bundle: TestBundle263 let event: TestEvent264}265266struct TestCaseEventRecord: Codable {267 let testCase: TestCase268 let event: TestEvent269}270271struct TestCaseFailureRecord: Codable, CustomStringConvertible {272 let testCase: TestCase273 let issue: TestIssue274 let failureKind: TestFailureKind275276 var description: String {277 return "\(issue.sourceCodeContext.description)\(testCase) \(issue.compactDescription)"278 }279}280281struct TestSuiteEventRecord: Codable {282 let suite: TestSuiteRecord283 let event: TestEvent284}285286struct TestSuiteFailureRecord: Codable {287 let suite: TestSuiteRecord288 let issue: TestIssue289 let failureKind: TestFailureKind290}291292293294struct TestBundle: Codable {295 let bundleIdentifier: String?296 let bundlePath: String297}298299struct TestCase: Codable {300 let name: String301}302303struct TestErrorInfo: Codable {304 let description: String305 let type: String306}307308enum TestEvent: Codable {309 case start310 case finish311}312313enum TestFailureKind: Codable, Equatable {314 case unexpected315 case expected(failureReason: String?)316317 var isExpected: Bool {318 switch self {319 case .expected: return true320 case .unexpected: return false321 }322 }323}324325struct TestIssue: Codable {326 let type: TestIssueType327 let compactDescription: String328 let detailedDescription: String?329 let associatedError: TestErrorInfo?330 let sourceCodeContext: TestSourceCodeContext331 let attachments: [TestAttachment]332}333334enum TestIssueType: Codable {335 case assertionFailure336 case performanceRegression337 case system338 case thrownError339 case uncaughtException340 case unmatchedExpectedFailure341 case unknown342}343344struct TestLocation: Codable, CustomStringConvertible {345 let file: String346 let line: Int347348 var description: String {349 return "\(file):\(line) "350 }351}352353struct TestSourceCodeContext: Codable, CustomStringConvertible {354 let callStack: [TestSourceCodeFrame]355 let location: TestLocation?356357 var description: String {358 return location?.description ?? ""359 }360}361362struct TestSourceCodeFrame: Codable {363 let address: UInt64364 let symbolInfo: TestSourceCodeSymbolInfo?365 let symbolicationError: TestErrorInfo?366}367368struct TestSourceCodeSymbolInfo: Codable {369 let imageName: String370 let symbolName: String371 let location: TestLocation?372}373374struct TestSuiteRecord: Codable {375 let name: String376}377378379380extension TestIssue {381 init(description: String, inFile filePath: String?, atLine lineNumber: Int) {382 let location: TestLocation?383 if let filePath = filePath {384 location = .init(file: filePath, line: lineNumber)385 } else {386 location = nil387 }388 self.init(type: .assertionFailure, compactDescription: description, detailedDescription: description, associatedError: nil, sourceCodeContext: .init(callStack: [], location: location), attachments: [])389 }390}391392import XCTest393394#if canImport(Darwin) 395extension TestAttachment {396 init(_ attachment: XCTAttachment) {397 self.init(398 name: attachment.name,399 uniformTypeIdentifier: attachment.uniformTypeIdentifier,400 payload: attachment.value(forKey: "payload") as? Data401 )402 }403}404#endif405406extension TestBundle {407 init(_ testBundle: Bundle) {408 self.init(409 bundleIdentifier: testBundle.bundleIdentifier,410 bundlePath: testBundle.bundlePath411 )412 }413}414415extension TestCase {416 init(_ testCase: XCTestCase) {417 self.init(name: testCase.name)418 }419}420421extension TestErrorInfo {422 init(_ error: any Swift.Error) {423 self.init(description: "\(error)", type: "\(Swift.type(of: error))")424 }425}426427#if canImport(Darwin) 428extension TestIssue {429 init(_ issue: XCTIssue) {430 self.init(431 type: .init(issue.type),432 compactDescription: issue.compactDescription,433 detailedDescription: issue.detailedDescription,434 associatedError: issue.associatedError.map { .init($0) },435 sourceCodeContext: .init(issue.sourceCodeContext),436 attachments: issue.attachments.map { .init($0) }437 )438 }439}440441extension TestIssueType {442 init(_ type: XCTIssue.IssueType) {443 switch type {444 case .assertionFailure: self = .assertionFailure445 case .thrownError: self = .thrownError446 case .uncaughtException: self = .uncaughtException447 case .performanceRegression: self = .performanceRegression448 case .system: self = .system449 case .unmatchedExpectedFailure: self = .unmatchedExpectedFailure450 @unknown default: self = .unknown451 }452 }453}454#endif455456#if canImport(Darwin) 457extension TestLocation {458 init(_ location: XCTSourceCodeLocation) {459 self.init(460 file: location.fileURL.absoluteString,461 line: location.lineNumber462 )463 }464}465466extension TestSourceCodeContext {467 init(_ context: XCTSourceCodeContext) {468 self.init(469 callStack: context.callStack.map { .init($0) },470 location: context.location.map { .init($0) }471 )472 }473}474475extension TestSourceCodeFrame {476 init(_ frame: XCTSourceCodeFrame) {477 self.init(478 address: frame.address,479 symbolInfo: (try? frame.symbolInfo()).map { .init($0) },480 symbolicationError: frame.symbolicationError.map { .init($0) }481 )482 }483}484485extension TestSourceCodeSymbolInfo {486 init(_ symbolInfo: XCTSourceCodeSymbolInfo) {487 self.init(488 imageName: symbolInfo.imageName,489 symbolName: symbolInfo.symbolName,490 location: symbolInfo.location.map { .init($0) }491 )492 }493}494#endif495496extension TestSuiteRecord {497 init(_ testSuite: XCTestSuite) {498 self.init(name: testSuite.name)499 }500}501502import XCTest503504#endif505506@main507@available(macOS 10.15, iOS 11, watchOS 4, tvOS 11, *)508@available(*, deprecated, message: "Not actually deprecated. Marked as deprecated to allow inclusion of deprecated tests (which test deprecated functionality) without warnings")509struct Runner {510 private static func testingLibrary() -> String {511 var iterator = CommandLine.arguments.makeIterator()512 while let argument = iterator.next() {513 if argument == "--testing-library", let libraryName = iterator.next() {514 return libraryName.lowercased()515 }516 }517518 519 return "xctest"520 }521522 #if false523524525 #endif526527 static func main() async {528 let testingLibrary = Self.testingLibrary()529 #if canImport(Testing)530 if testingLibrary == "swift-testing" {531 #if false532533534535 #else536 await Testing.__swiftPMEntryPoint() as Never537 #endif538 }539 #endif540 #if false541542543544545 #endif546 }547}