@@ -6,44 +6,72 @@ struct TestLibrary: Generator {
66 import XCTest
77 import class Foundation.Bundle
88
9+ public protocol TimeLimit {
10+ var timeLimit: TimeInterval { get }
11+ }
12+
913 public typealias TestCase = (file: StaticString, line: UInt, input: String, expected: String)
1014
11- public extension XCTestCase {
15+ public extension TimeLimit where Self: XCTestCase {
1216 func solve(file: StaticString, line: UInt, input: String, expected: String) throws {
1317 // Some of the APIs that we use below are available in macOS 10.13 and above.
1418 guard #available(macOS 10.13, *) else {
1519 return
1620 }
17-
18- let testTarget = String(describing: type(of: self)).replacingOccurrences(of: \" Tests \" , with: \" \" )
21+ var error = " "
22+ let exp = expectation(description: " " )
23+ let testTarget = String(describing: type(of: self)).replacingOccurrences(of: " Tests " , with: " " )
1924 let binary = productsDirectory.appendingPathComponent(testTarget)
2025 let process = Process()
26+
27+ addTeardownBlock {
28+ if process.isRunning {
29+ process.terminate()
30+ }
31+ }
32+
2133 process.executableURL = binary
2234 let pipeInput = Pipe()
2335 process.standardInput = pipeInput
2436 let pipeOutput = Pipe()
2537 process.standardOutput = pipeOutput
2638 let pipeError = Pipe()
2739 process.standardError = pipeError
28- try process.run()
29- pipeInput.fileHandleForWriting.write(input.data(using: .utf8)!)
30- pipeInput.fileHandleForWriting.closeFile()
31- process.waitUntilExit()
32- let error = String(data: pipeError.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)!
33- if !error.isEmpty {
34- XCTFail(error, file: file, line: line)
35- return
40+
41+ DispatchQueue.global().async {
42+ do {
43+ try process.run()
44+ pipeInput.fileHandleForWriting.write(input.data(using: .utf8)!)
45+ pipeInput.fileHandleForWriting.closeFile()
46+ process.waitUntilExit()
47+ exp.fulfill()
48+ error = String(data: pipeError.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)!
49+ } catch (let e) {
50+ error = e.localizedDescription
51+ }
52+ }
53+ let result = XCTWaiter.wait(for: [exp], timeout: timeLimit)
54+ switch result {
55+ case .completed:
56+ if error.isEmpty {
57+ let data = pipeOutput.fileHandleForReading.readDataToEndOfFile()
58+ let output = String(data: data, encoding: .utf8)!
59+ XCTAssertEqual(output, expected, file: file, line: line)
60+ } else {
61+ XCTFail( " RE: \\ (error) " , file: file, line: line)
62+ }
63+ case .timedOut:
64+ XCTFail( " TLE: Exceeded timeout of \\ (timeLimit) seconds " , file: file, line: line)
65+ default:
66+ XCTFail( " Unrecognized error. " , file: file, line: line)
3667 }
37- let data = pipeOutput.fileHandleForReading.readDataToEndOfFile()
38- let output = String(data: data, encoding: .utf8)!
39- XCTAssertEqual(output, expected, file: file, line: line)
4068 }
4169
4270 private var productsDirectory: URL {
43- for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix( \ ".xctest \ ") {
71+ for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix( " .xctest " ) {
4472 return bundle.bundleURL.deletingLastPathComponent()
4573 }
46- fatalError( \ "couldn't find the products directory \ ")
74+ fatalError( " couldn't find the products directory " )
4775 }
4876 }
4977 """
0 commit comments