summaryrefslogtreecommitdiff
path: root/SoraTests/XCTestCase+SourceAnalysis.swift
diff options
context:
space:
mode:
Diffstat (limited to 'SoraTests/XCTestCase+SourceAnalysis.swift')
-rw-r--r--SoraTests/XCTestCase+SourceAnalysis.swift210
1 files changed, 210 insertions, 0 deletions
diff --git a/SoraTests/XCTestCase+SourceAnalysis.swift b/SoraTests/XCTestCase+SourceAnalysis.swift
new file mode 100644
index 0000000..4cd907f
--- /dev/null
+++ b/SoraTests/XCTestCase+SourceAnalysis.swift
@@ -0,0 +1,210 @@
+import Foundation
+import XCTest
+
+extension XCTestCase {
+ func loadSource(at relativePath: String) throws -> String {
+ let currentFile = URL(fileURLWithPath: #filePath)
+ let repositoryRoot = currentFile.deletingLastPathComponent().deletingLastPathComponent()
+ let fileURL = repositoryRoot.appendingPathComponent(relativePath)
+
+ return try String(contentsOf: fileURL, encoding: .utf8)
+ }
+
+ // swiftlint:disable:next cyclomatic_complexity
+ func extractFunction(named signature: String, from source: String) throws -> String {
+ guard let signatureRange = source.range(of: signature) else {
+ throw NSError(domain: "SourceAnalysisTests", code: 1)
+ }
+ guard let openingBrace = source[signatureRange.upperBound...].firstIndex(of: "{") else {
+ throw NSError(domain: "SourceAnalysisTests", code: 2)
+ }
+
+ let characters = Array(source)
+ let startOffset = source.distance(from: source.startIndex, to: signatureRange.lowerBound)
+ var currentOffset = source.distance(from: source.startIndex, to: openingBrace)
+
+ var braceDepth = 0
+ var inLineComment = false
+ var blockCommentDepth = 0
+ var inString = false
+ var isEscaped = false
+
+ while currentOffset < characters.count {
+ let current = characters[currentOffset]
+ let next: Character? = currentOffset + 1 < characters.count ? characters[currentOffset + 1] : nil
+
+ if inLineComment {
+ if current == "\n" {
+ inLineComment = false
+ }
+ currentOffset += 1
+ continue
+ }
+
+ if blockCommentDepth > 0 {
+ if current == "/", next == "*" {
+ blockCommentDepth += 1
+ currentOffset += 2
+ continue
+ }
+
+ if current == "*", next == "/" {
+ blockCommentDepth -= 1
+ currentOffset += 2
+ continue
+ }
+
+ currentOffset += 1
+ continue
+ }
+
+ if inString {
+ if isEscaped {
+ isEscaped = false
+ } else if current == "\\" {
+ isEscaped = true
+ } else if current == "\"" {
+ inString = false
+ }
+
+ currentOffset += 1
+ continue
+ }
+
+ if current == "/", next == "/" {
+ inLineComment = true
+ currentOffset += 2
+ continue
+ }
+
+ if current == "/", next == "*" {
+ blockCommentDepth = 1
+ currentOffset += 2
+ continue
+ }
+
+ if current == "\"" {
+ inString = true
+ currentOffset += 1
+ continue
+ }
+
+ if current == "{" {
+ braceDepth += 1
+ } else if current == "}" {
+ braceDepth -= 1
+
+ if braceDepth == 0 {
+ let endIndex = source.index(source.startIndex, offsetBy: currentOffset + 1)
+ let startIndex = source.index(source.startIndex, offsetBy: startOffset)
+
+ return String(source[startIndex..<endIndex])
+ }
+ }
+
+ currentOffset += 1
+ }
+
+ throw NSError(domain: "SourceAnalysisTests", code: 3)
+ }
+
+ // swiftlint:disable:next cyclomatic_complexity
+ func strippingCommentsAndStrings(from source: String) -> String {
+ let characters = Array(source)
+ var result: [Character] = []
+ result.reserveCapacity(characters.count)
+
+ var currentOffset = 0
+ var inLineComment = false
+ var blockCommentDepth = 0
+ var inString = false
+ var isEscaped = false
+
+ while currentOffset < characters.count {
+ let current = characters[currentOffset]
+ let next: Character? = currentOffset + 1 < characters.count ? characters[currentOffset + 1] : nil
+
+ if inLineComment {
+ if current == "\n" {
+ inLineComment = false
+ result.append("\n")
+ } else {
+ result.append(" ")
+ }
+ currentOffset += 1
+ continue
+ }
+
+ if blockCommentDepth > 0 {
+ if current == "/", next == "*" {
+ blockCommentDepth += 1
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ if current == "*", next == "/" {
+ blockCommentDepth -= 1
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ result.append(current == "\n" ? "\n" : " ")
+ currentOffset += 1
+ continue
+ }
+
+ if inString {
+ if isEscaped {
+ isEscaped = false
+ } else if current == "\\" {
+ isEscaped = true
+ } else if current == "\"" {
+ inString = false
+ }
+
+ result.append(current == "\n" ? "\n" : " ")
+ currentOffset += 1
+ continue
+ }
+
+ if current == "/", next == "/" {
+ inLineComment = true
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ if current == "/", next == "*" {
+ blockCommentDepth = 1
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ if current == "\"" {
+ inString = true
+ result.append(" ")
+ currentOffset += 1
+ continue
+ }
+
+ result.append(current)
+ currentOffset += 1
+ }
+
+ return String(result)
+ }
+
+ func tokenCount(matching pattern: String, in source: String) -> Int {
+ guard let regex = try? NSRegularExpression(pattern: pattern) else { return 0 }
+ let range = NSRange(source.startIndex..<source.endIndex, in: source)
+
+ return regex.numberOfMatches(in: source, range: range)
+ }
+}