From e3d6022efa3d6b649aa9fd8c5294849f54cd4c89 Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 24 Nov 2020 03:58:46 -0800 Subject: [PATCH 01/11] Remove LinuxMain --- Tests/LinuxMain.swift | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 Tests/LinuxMain.swift diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index 22c49c5..0000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,7 +0,0 @@ -import XCTest - -import doctestTests - -var tests = [XCTestCaseEntry]() -tests += doctestTests.allTests() -XCTMain(tests) From cbab7fae3f43abed873fd2913ce8d2d88f543f69 Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 24 Nov 2020 04:53:33 -0800 Subject: [PATCH 02/11] Update expectations to type, value, match, and error --- Sources/DocTest/Expectation.swift | 39 +++++++++++++++---- .../StringProtocol+Extensions.swift | 7 ++++ Sources/DocTest/REPL.swift | 6 +-- Sources/DocTest/Statement.swift | 20 ++++++---- Tests/DocTestTests/DocTestTests.swift | 15 +++++-- 5 files changed, 64 insertions(+), 23 deletions(-) create mode 100644 Sources/DocTest/Extensions/StringProtocol+Extensions.swift diff --git a/Sources/DocTest/Expectation.swift b/Sources/DocTest/Expectation.swift index 6ef9200..31656ad 100644 --- a/Sources/DocTest/Expectation.swift +++ b/Sources/DocTest/Expectation.swift @@ -1,19 +1,42 @@ import Foundation public enum Expectation: Hashable { - case value(String) case error + case type(String) + case value(String) + case match(String) public init?(_ string: String?) { - guard let string = string?.trimmingCharacters(in: .whitespacesAndNewlines) else { return nil } - if string.starts(with: "=>"), - let index = string.firstIndex(where: { $0.isWhitespace }) - { - self = .value(string.suffix(from: index).trimmingCharacters(in: .whitespaces)) - } else if string.starts(with: "!!") { + guard let string = string?.trimmed, + let index = string.index(string.startIndex, offsetBy: 2, limitedBy: string.endIndex) + else { return nil } + + switch string.prefix(upTo: index) { + case "!!": self = .error - } else { + case "->": + self = .type(string.suffix(from: index).trimmed) + case "=>": + self = .value(string.suffix(from: index).trimmed) + case "~>": + self = .match(string.suffix(from: index).trimmed) + default: return nil } } + + public func evaluate(_ output: String) -> Bool { + let output = output.trimmed + + switch self { + case .error: + return output.hasPrefix("error:") + case .type(let type): + return output.hasPrefix("\(type) =") + case .value(let value): + return output.hasSuffix("= \(value)") + case .match(let pattern): + return output.range(of: pattern, options: .regularExpression) != nil + } + } } diff --git a/Sources/DocTest/Extensions/StringProtocol+Extensions.swift b/Sources/DocTest/Extensions/StringProtocol+Extensions.swift new file mode 100644 index 0000000..285ddad --- /dev/null +++ b/Sources/DocTest/Extensions/StringProtocol+Extensions.swift @@ -0,0 +1,7 @@ +import Foundation + +extension StringProtocol { + var trimmed: String { + trimmingCharacters(in: .whitespacesAndNewlines) + } +} diff --git a/Sources/DocTest/REPL.swift b/Sources/DocTest/REPL.swift index df2f876..8a9af72 100644 --- a/Sources/DocTest/REPL.swift +++ b/Sources/DocTest/REPL.swift @@ -17,9 +17,9 @@ public class REPL { public var description: String public init?(_ description: String) { - let trimmedDescription = description.trimmingCharacters(in: .whitespacesAndNewlines) - guard !trimmedDescription.isEmpty else { return nil } - self.description = trimmedDescription + let description = description.trimmed + guard !description.isEmpty else { return nil } + self.description = description } } diff --git a/Sources/DocTest/Statement.swift b/Sources/DocTest/Statement.swift index b111a91..008462b 100644 --- a/Sources/DocTest/Statement.swift +++ b/Sources/DocTest/Statement.swift @@ -8,7 +8,7 @@ public class Statement { public internal(set) var expectations: [Expectation] = [] public init?(_ node: CodeBlockItemSyntax, _ sourceLocation: SourceLocation) { - let code = node.withoutTrivia().description.trimmingCharacters(in: .whitespacesAndNewlines) + let code = node.withoutTrivia().description.trimmed guard !code.isEmpty else { return nil } self.code = code @@ -31,16 +31,18 @@ public class Statement { } else { return expectations.map { expectation in switch expectation { - case .value(let expected): + case .error: + return test { + .success("- `\(self.code)` produced an error, as expected", directive: nil, metadata: metadata) + } + case .type(let expected), + .value(let expected), + .match(let expected): metadata["expected"] = expected return test { .failure("- `\(self.code)` produced an error", directive: nil, metadata: metadata) } - case .error: - return test { - .success("- `\(self.code)` produced an error, as expected", directive: nil, metadata: metadata) - } } } } @@ -49,10 +51,12 @@ public class Statement { return expectations.map { expectation in switch expectation { - case .value(let expected): + case .type(let expected), + .value(let expected), + .match(let expected): metadata["expected"] = expected - if actual == expected { + if expectation.evaluate(actual) { return test { .success("- `\(self.code)` produces `\(actual)`", directive: nil, metadata: metadata) } diff --git a/Tests/DocTestTests/DocTestTests.swift b/Tests/DocTestTests/DocTestTests.swift index 105931d..cd2a51f 100644 --- a/Tests/DocTestTests/DocTestTests.swift +++ b/Tests/DocTestTests/DocTestTests.swift @@ -4,11 +4,11 @@ import DocTest final class DocTestTests: XCTestCase { func testRunner() throws { let source = #""" - 1 + 1 // => Int = 2 - 1 + 1 // => String = "wat" - 1 / 0 // !! Error + 1 + 1 // => 2 + 1 + 1 // -> String + 1 / 0 // !! error: division by zero invalid - 1 + 1 // => Int = 2 + 1 + 1 // ~> Int = \d """# let expectation = XCTestExpectation() @@ -31,4 +31,11 @@ final class DocTestTests: XCTestCase { } wait(for: [expectation], timeout: 10.0) } + + func testExpectations() throws { + XCTAssertEqual(Expectation("-> Int"), .type("Int")) + XCTAssertEqual(Expectation("=> 2"), .value("2")) + XCTAssertEqual(Expectation(#"~> Int = \d+"#), .match(#"Int = \d+"#)) + XCTAssertEqual(Expectation("!! error: division by zero"), .error) + } } From 61432e4e828b68e4cb02039dc9ece7eeb4695fe4 Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 24 Nov 2020 05:16:05 -0800 Subject: [PATCH 03/11] Refactor scanning logic into Scanner type --- Package.swift | 2 +- Sources/DocTest/Scanner.swift | 41 ++++++++++++++++++++++++++++++++ Sources/swift-doctest/main.swift | 25 ++++--------------- 3 files changed, 46 insertions(+), 22 deletions(-) create mode 100644 Sources/DocTest/Scanner.swift diff --git a/Package.swift b/Package.swift index 5a5c24c..84dda23 100644 --- a/Package.swift +++ b/Package.swift @@ -34,7 +34,6 @@ let package = Package( name: "swift-doctest", dependencies: [ .target(name: "DocTest"), - .product(name: "StringLocationConverter", package: "StringLocationConverter"), .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Logging", package: "swift-log"), ]), @@ -42,6 +41,7 @@ let package = Package( name: "DocTest", dependencies: [ .product(name: "SwiftSyntax", package: "SwiftSyntax"), + .product(name: "StringLocationConverter", package: "StringLocationConverter"), .product(name: "TAP", package: "TAP"), ]), .testTarget( diff --git a/Sources/DocTest/Scanner.swift b/Sources/DocTest/Scanner.swift new file mode 100644 index 0000000..ad980e6 --- /dev/null +++ b/Sources/DocTest/Scanner.swift @@ -0,0 +1,41 @@ +import Foundation +import StringLocationConverter + +public class Scanner { + public typealias Match = (line: Int, column: Int, content: String) + + private var regularExpression: NSRegularExpression + + public init() throws { + let pattern = #"^\`{3}\s*swift\s+doctest\s*\n(.+)\n\`{3}$"# + self.regularExpression = try NSRegularExpression(pattern: pattern, + options: [ + .caseInsensitive, + .anchorsMatchLines, + .dotMatchesLineSeparators + ]) + } + + public func matches(in source: String) -> [Match] { + let range = NSRange(source.startIndex.. Date: Tue, 24 Nov 2020 05:27:58 -0800 Subject: [PATCH 04/11] Fix Scanner regular expression pattern Add test for Scanner --- Sources/DocTest/Scanner.swift | 11 ++++-- Tests/DocTestTests/DocTestTests.swift | 48 ++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/Sources/DocTest/Scanner.swift b/Sources/DocTest/Scanner.swift index ad980e6..7cf227c 100644 --- a/Sources/DocTest/Scanner.swift +++ b/Sources/DocTest/Scanner.swift @@ -7,11 +7,18 @@ public class Scanner { private var regularExpression: NSRegularExpression public init() throws { - let pattern = #"^\`{3}\s*swift\s+doctest\s*\n(.+)\n\`{3}$"# + let pattern = #""" + ^ + \h* \`{3} \h* swift \h+ doctest \h* \n + (.+)\n + \h* \`{3} \h* + $ + """# self.regularExpression = try NSRegularExpression(pattern: pattern, options: [ - .caseInsensitive, + .allowCommentsAndWhitespace, .anchorsMatchLines, + .caseInsensitive, .dotMatchesLineSeparators ]) } diff --git a/Tests/DocTestTests/DocTestTests.swift b/Tests/DocTestTests/DocTestTests.swift index cd2a51f..ffca277 100644 --- a/Tests/DocTestTests/DocTestTests.swift +++ b/Tests/DocTestTests/DocTestTests.swift @@ -4,11 +4,11 @@ import DocTest final class DocTestTests: XCTestCase { func testRunner() throws { let source = #""" - 1 + 1 // => 2 - 1 + 1 // -> String + 1 + 2 // => 3 + 1 + 2 // -> String 1 / 0 // !! error: division by zero invalid - 1 + 1 // ~> Int = \d + 1 + 2 // ~> Int = \d """# let expectation = XCTestExpectation() @@ -20,11 +20,11 @@ final class DocTestTests: XCTestCase { XCTFail("\(error)") case .success(let report): XCTAssertEqual(report.results.count, 5) - XCTAssertTrue(try! report.results[0].get().ok) // 1 + 1 => 2 - XCTAssertFalse(try! report.results[1].get().ok) // 1 + 1 => "wat" - XCTAssertTrue(try! report.results[2].get().ok) // 1 / 0 !! Error - XCTAssertFalse(try! report.results[3].get().ok) // invalid - XCTAssertTrue(try! report.results[4].get().ok) // 1 + 1 => 2 + XCTAssertTrue(try! report.results[0].get().ok) + XCTAssertFalse(try! report.results[1].get().ok) + XCTAssertTrue(try! report.results[2].get().ok) + XCTAssertFalse(try! report.results[3].get().ok) + XCTAssertTrue(try! report.results[4].get().ok) expectation.fulfill() } @@ -32,6 +32,38 @@ final class DocTestTests: XCTestCase { wait(for: [expectation], timeout: 10.0) } + func testScanner() throws { + let scanner = try Scanner() + + + let source = #""" + /** + Returns the sum of two integers. + + ```swift doctest + add(1, 3) // => 2 + ``` + */ + func add(_ a: Int, _ b: Int) -> Int { + return a + b + } + + /** + Returns the product of two integers. + */ + func multiply(_ a: Int, _ b: Int) -> Int { + return a * b + } + """# + + let matches = scanner.matches(in: source) + + XCTAssertEqual(matches.count, 1) + XCTAssertEqual(matches.first?.line, 5) + XCTAssertEqual(matches.first?.column, 1) + XCTAssertEqual(matches.first?.content, "add(1, 3) // => 2") + } + func testExpectations() throws { XCTAssertEqual(Expectation("-> Int"), .type("Int")) XCTAssertEqual(Expectation("=> 2"), .value("2")) From 2cb109e67e54c68a13dccbc9fe02dfdc7c917dc9 Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 24 Nov 2020 05:33:17 -0800 Subject: [PATCH 05/11] Internalize inessential APIs --- Sources/DocTest/REPL.swift | 9 ++++----- Sources/DocTest/Statement.swift | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Sources/DocTest/REPL.swift b/Sources/DocTest/REPL.swift index 8a9af72..9782aea 100644 --- a/Sources/DocTest/REPL.swift +++ b/Sources/DocTest/REPL.swift @@ -41,7 +41,7 @@ public class REPL { public var evaluationHandler: ((Statement, Result) -> Void)? - public init(configuration: Configuration) { + init(configuration: Configuration) { process = Process() if #available(OSX 10.13, *) { @@ -108,7 +108,7 @@ public class REPL { } } - public func evaluate(_ statement: Statement) { + func evaluate(_ statement: Statement) { if !process.isRunning { if #available(OSX 10.13, *) { try! process.run() @@ -133,12 +133,11 @@ public class REPL { outputPipe.fileHandleForReading.readabilityHandler = nil } - public func waitUntilExit() { + func waitUntilExit() { process.waitUntilExit() } - public func close() { - + func close() { if #available(OSX 10.15, *) { try! self.inputPipe.fileHandleForWriting.close() } diff --git a/Sources/DocTest/Statement.swift b/Sources/DocTest/Statement.swift index 008462b..cbc42b1 100644 --- a/Sources/DocTest/Statement.swift +++ b/Sources/DocTest/Statement.swift @@ -15,7 +15,7 @@ public class Statement { self.sourceLocation = sourceLocation } - public func tests(with result: Result) -> [Test] { + func tests(with result: Result) -> [Test] { var metadata: [String: Any] = [ "file": self.sourceLocation.file as Any?, "line": self.sourceLocation.line as Any?, From ee3265bc411cb12bbe8f65386b82df48dbe2a352 Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 24 Nov 2020 05:33:55 -0800 Subject: [PATCH 06/11] Extract tests into separate files --- Tests/DocTestTests/DocTestTests.swift | 73 ----------------------- Tests/DocTestTests/ExpectationTests.swift | 24 ++++++++ Tests/DocTestTests/RunnerTests.swift | 34 +++++++++++ Tests/DocTestTests/ScannerTests.swift | 36 +++++++++++ 4 files changed, 94 insertions(+), 73 deletions(-) delete mode 100644 Tests/DocTestTests/DocTestTests.swift create mode 100644 Tests/DocTestTests/ExpectationTests.swift create mode 100644 Tests/DocTestTests/RunnerTests.swift create mode 100644 Tests/DocTestTests/ScannerTests.swift diff --git a/Tests/DocTestTests/DocTestTests.swift b/Tests/DocTestTests/DocTestTests.swift deleted file mode 100644 index ffca277..0000000 --- a/Tests/DocTestTests/DocTestTests.swift +++ /dev/null @@ -1,73 +0,0 @@ -import XCTest -import DocTest - -final class DocTestTests: XCTestCase { - func testRunner() throws { - let source = #""" - 1 + 2 // => 3 - 1 + 2 // -> String - 1 / 0 // !! error: division by zero - invalid - 1 + 2 // ~> Int = \d - """# - - let expectation = XCTestExpectation() - - let runner = try Runner(source: source, assumedFileName: "Example.swift") - runner.run(with: .default) { (result) in - switch result { - case .failure(let error): - XCTFail("\(error)") - case .success(let report): - XCTAssertEqual(report.results.count, 5) - XCTAssertTrue(try! report.results[0].get().ok) - XCTAssertFalse(try! report.results[1].get().ok) - XCTAssertTrue(try! report.results[2].get().ok) - XCTAssertFalse(try! report.results[3].get().ok) - XCTAssertTrue(try! report.results[4].get().ok) - - expectation.fulfill() - } - } - wait(for: [expectation], timeout: 10.0) - } - - func testScanner() throws { - let scanner = try Scanner() - - - let source = #""" - /** - Returns the sum of two integers. - - ```swift doctest - add(1, 3) // => 2 - ``` - */ - func add(_ a: Int, _ b: Int) -> Int { - return a + b - } - - /** - Returns the product of two integers. - */ - func multiply(_ a: Int, _ b: Int) -> Int { - return a * b - } - """# - - let matches = scanner.matches(in: source) - - XCTAssertEqual(matches.count, 1) - XCTAssertEqual(matches.first?.line, 5) - XCTAssertEqual(matches.first?.column, 1) - XCTAssertEqual(matches.first?.content, "add(1, 3) // => 2") - } - - func testExpectations() throws { - XCTAssertEqual(Expectation("-> Int"), .type("Int")) - XCTAssertEqual(Expectation("=> 2"), .value("2")) - XCTAssertEqual(Expectation(#"~> Int = \d+"#), .match(#"Int = \d+"#)) - XCTAssertEqual(Expectation("!! error: division by zero"), .error) - } -} diff --git a/Tests/DocTestTests/ExpectationTests.swift b/Tests/DocTestTests/ExpectationTests.swift new file mode 100644 index 0000000..a8cae44 --- /dev/null +++ b/Tests/DocTestTests/ExpectationTests.swift @@ -0,0 +1,24 @@ +import XCTest +import DocTest + +final class ExpectationTests: XCTestCase { + func testTypeExpectation() throws { + XCTAssertEqual(Expectation("-> Int"), .type("Int")) + } + + func testValueExpectation() throws { + XCTAssertEqual(Expectation("=> 2"), .value("2")) + } + + func testMatchExpectation() throws { + XCTAssertEqual(Expectation(#"~> Int = \d+"#), .match(#"Int = \d+"#)) + } + + func testErrorExpectation() throws { + XCTAssertEqual(Expectation("!! error: division by zero"), .error) + } + + func testInvalidExpectation() throws { + XCTAssertNil(Expectation("invalid")) + } +} diff --git a/Tests/DocTestTests/RunnerTests.swift b/Tests/DocTestTests/RunnerTests.swift new file mode 100644 index 0000000..90d40a3 --- /dev/null +++ b/Tests/DocTestTests/RunnerTests.swift @@ -0,0 +1,34 @@ +import XCTest +import DocTest + +final class RunnerTests: XCTestCase { + func testExample() throws { + let source = #""" + 1 + 2 // => 3 + 1 + 2 // -> String + 1 / 0 // !! error: division by zero + invalid + 1 + 2 // ~> Int = \d + """# + + let expectation = XCTestExpectation() + + let runner = try Runner(source: source, assumedFileName: "Example.swift") + runner.run(with: .default) { (result) in + switch result { + case .failure(let error): + XCTFail("\(error)") + case .success(let report): + XCTAssertEqual(report.results.count, 5) + XCTAssertTrue(try! report.results[0].get().ok) + XCTAssertFalse(try! report.results[1].get().ok) + XCTAssertTrue(try! report.results[2].get().ok) + XCTAssertFalse(try! report.results[3].get().ok) + XCTAssertTrue(try! report.results[4].get().ok) + + expectation.fulfill() + } + } + wait(for: [expectation], timeout: 10.0) + } +} diff --git a/Tests/DocTestTests/ScannerTests.swift b/Tests/DocTestTests/ScannerTests.swift new file mode 100644 index 0000000..5c5dd94 --- /dev/null +++ b/Tests/DocTestTests/ScannerTests.swift @@ -0,0 +1,36 @@ +import XCTest +import DocTest + +final class ScannerTests: XCTestCase { + func testExample() throws { + let scanner = try Scanner() + + + let source = #""" + /** + Returns the sum of two integers. + + ```swift doctest + add(1, 3) // => 2 + ``` + */ + func add(_ a: Int, _ b: Int) -> Int { + return a + b + } + + /** + Returns the product of two integers. + */ + func multiply(_ a: Int, _ b: Int) -> Int { + return a * b + } + """# + + let matches = scanner.matches(in: source) + + XCTAssertEqual(matches.count, 1) + XCTAssertEqual(matches.first?.line, 5) + XCTAssertEqual(matches.first?.column, 1) + XCTAssertEqual(matches.first?.content, "add(1, 3) // => 2") + } +} From 2ad8ef389488a407eb7f72e88f68c001f967bd02 Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 24 Nov 2020 05:43:45 -0800 Subject: [PATCH 07/11] Add changelog entries for #30 --- Changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3541bca..6bd5e22 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Changed Swift version requirement to 5.3. #29 by @mattt. +- Changed expression syntax. + #30 by @mattt. + +### Fixed + +- Fixed a bug that caused DocTest annotations to be missed. + #30 by @mattt. ## [0.1.0] - 2020-05-04 From 10ab576d1e92c72cad4b4942b7d6b7fd841f4c8f Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 24 Nov 2020 05:47:58 -0800 Subject: [PATCH 08/11] Update README --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c3d9c41..13f6861 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ OVERVIEW: A utility for syntax testing documentation in Swift code. USAGE: swift-doctest [--swift-launch-path ] [--package] [--assumed-filename ] ARGUMENTS: - Swift code or a path to a Swift file + Swift code or a path to a Swift file OPTIONS: --swift-launch-path @@ -130,22 +130,20 @@ This tells the documentation test runner to evaluate the code sample. ``` By adding an annotation in the format -`=> (Type) = (Value)`, +`=> <#Value#>`, we can test the expected type and value of the expression. ```diff - add(1 1) // 3.0 -+ add(1 1) // => Double = 3.0 ++ add(1 1) // => 3.0 ``` Run the `swift-doctest` command -from the root directory of the Swift package, -specifying the `--package` flag -(to invoke the Swift REPL via the Swift Package Manager) +from the root directory of the Swift package and passing the path to the file containing the `add(_:_:)` function. This will scan for all of code blocks annotated with -```swift doctest +```swift doctest, run them through the Swift REPL, and test the output with any annotated expectations. @@ -153,7 +151,7 @@ and test the output with any annotated expectations. $ swift doctest --package path/to/file.swift TAP version 13 1..1 -not ok 1 - `add(1 1)` did not produce `Double = 3.0` +not ok 1 - `add(1 1)` did not produce `3.0` --- column: 1 file: path/to/file.swift.md @@ -172,7 +170,7 @@ we update the documentation to fix the example. Returns the sum of two integers. ```swift doctest - add(1, 1) // => Int = 2 + add(1, 1) // => 2 ``` */ func add(_ a: Int, _ b: Int) -> Int { ... } @@ -185,7 +183,7 @@ the tests now pass as expected. $ swift doctest --package path/to/file.swift TAP version 13 1..1 -ok 1 - `add(1, 1)` produces `Int = 2` +ok 1 - `add(1, 1)` produces `2` --- column: 1 file: path/to/file.swift.md From 222a9e64046a90345bf07e269be3454993f1bda3 Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 24 Nov 2020 05:49:09 -0800 Subject: [PATCH 09/11] Add CI badge to README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 13f6861..7b591da 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # DocTest +![CI][ci badge] + **DocTest** is an experimental tool for testing Swift example code in documentation. @@ -208,3 +210,5 @@ Mattt ([@mattt](https://twitter.com/mattt)) [seccomp]: https://docs.docker.com/engine/security/seccomp/ [apparmor]: https://docs.docker.com/engine/security/apparmor/ + +[ci badge]: https://github.com/SwiftDocOrg/DocTest/workflows/CI/badge.svg From e19729c00a00b5416aaec9a24d567a8898fa105f Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 24 Nov 2020 06:11:28 -0800 Subject: [PATCH 10/11] Feed source file into REPL when not running in package context --- Sources/DocTest/Runner.swift | 8 ++++---- Sources/swift-doctest/main.swift | 9 ++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Sources/DocTest/Runner.swift b/Sources/DocTest/Runner.swift index aa2df3d..4c70507 100644 --- a/Sources/DocTest/Runner.swift +++ b/Sources/DocTest/Runner.swift @@ -37,14 +37,14 @@ public final class Runner { let repl = REPL(configuration: configuration) - repl.evaluationHandler = { (statement, result) in - tests.append(contentsOf: statement.tests(with: result)) - } - for statement in statements { repl.evaluate(statement) } + repl.evaluationHandler = { (statement, result) in + tests.append(contentsOf: statement.tests(with: result)) + } + repl.close() repl.waitUntilExit() diff --git a/Sources/swift-doctest/main.swift b/Sources/swift-doctest/main.swift index d0956c9..f1cfd52 100644 --- a/Sources/swift-doctest/main.swift +++ b/Sources/swift-doctest/main.swift @@ -87,7 +87,14 @@ struct SwiftDocTest: ParsableCommand { for match in scanner.matches(in: source) { logger.info("Found DocTest block at \(assumedFileName)#\(match.line):\(match.column)\n\(match.content)") - let runner = try Runner(source: match.content, assumedFileName: assumedFileName, lineOffset: match.line) + var lineOffset = match.line + var code = match.content + if !options.runThroughPackageManager { + code = "\(source)\n\(code)" + lineOffset -= source.split(whereSeparator: { $0.isNewline }).count + 1 + } + + let runner = try Runner(source: code, assumedFileName: assumedFileName, lineOffset: lineOffset) group.enter() runner.run(with: configuration) { result in switch result { From 9c4c1a8666c80c90294b1de50315b37fdf316399 Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 24 Nov 2020 06:19:30 -0800 Subject: [PATCH 11/11] Log notice for blocks without any import statements when running through SPM --- Sources/swift-doctest/main.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/swift-doctest/main.swift b/Sources/swift-doctest/main.swift index f1cfd52..f7b64d7 100644 --- a/Sources/swift-doctest/main.swift +++ b/Sources/swift-doctest/main.swift @@ -89,7 +89,11 @@ struct SwiftDocTest: ParsableCommand { var lineOffset = match.line var code = match.content - if !options.runThroughPackageManager { + if options.runThroughPackageManager { + if source.range(of: #"^import \w"#, options: [.regularExpression]) == nil { + logger.notice("No import statements found at \(assumedFileName)#\(match.line):\(match.column). This may cause unexpected API resolution failures when running through Swift Package Manager.") + } + } else { code = "\(source)\n\(code)" lineOffset -= source.split(whereSeparator: { $0.isNewline }).count + 1 }