1- @_spi ( Internal) import SwiftFormat
1+ @_spi ( Internal) @ _spi ( Testing ) import SwiftFormat
22import XCTest
33
4+ extension URL {
5+ /// Assuming this is a file URL, resolves all symlinks in the path.
6+ ///
7+ /// - Note: We need this because `URL.resolvingSymlinksInPath()` not only resolves symlinks but also standardizes the
8+ /// path by stripping away `private` prefixes. Since sourcekitd is not performing this standardization, using
9+ /// `resolvingSymlinksInPath` can lead to slightly mismatched URLs between the sourcekit-lsp response and the test
10+ /// assertion.
11+ fileprivate var realpath : URL {
12+ #if canImport(Darwin)
13+ return self . path. withCString { path in
14+ guard let realpath = Darwin . realpath ( path, nil ) else {
15+ return self
16+ }
17+ let result = URL ( fileURLWithPath: String ( cString: realpath) )
18+ free ( realpath)
19+ return result
20+ }
21+ #else
22+ // Non-Darwin platforms don't have the `/private` stripping issue, so we can just use `self.resolvingSymlinksInPath`
23+ // here.
24+ return self . resolvingSymlinksInPath ( )
25+ #endif
26+ }
27+ }
28+
429final class FileIteratorTests : XCTestCase {
530 private var tmpdir : URL !
631
@@ -10,7 +35,7 @@ final class FileIteratorTests: XCTestCase {
1035 in: . userDomainMask,
1136 appropriateFor: FileManager . default. temporaryDirectory,
1237 create: true
13- )
38+ ) . realpath
1439
1540 // Create a simple file tree used by the tests below.
1641 try touch ( " project/real1.swift " )
@@ -31,8 +56,8 @@ final class FileIteratorTests: XCTestCase {
3156 #endif
3257 let seen = allFilesSeen ( iteratingOver: [ tmpdir] , followSymlinks: false )
3358 XCTAssertEqual ( seen. count, 2 )
34- XCTAssertTrue ( seen. contains { $0. hasSuffix ( " project/real1.swift " ) } )
35- XCTAssertTrue ( seen. contains { $0. hasSuffix ( " project/real2.swift " ) } )
59+ XCTAssertTrue ( seen. contains { $0. path . hasSuffix ( " project/real1.swift " ) } )
60+ XCTAssertTrue ( seen. contains { $0. path . hasSuffix ( " project/real2.swift " ) } )
3661 }
3762
3863 func testFollowSymlinks( ) throws {
@@ -41,10 +66,10 @@ final class FileIteratorTests: XCTestCase {
4166 #endif
4267 let seen = allFilesSeen ( iteratingOver: [ tmpdir] , followSymlinks: true )
4368 XCTAssertEqual ( seen. count, 3 )
44- XCTAssertTrue ( seen. contains { $0. hasSuffix ( " project/real1.swift " ) } )
45- XCTAssertTrue ( seen. contains { $0. hasSuffix ( " project/real2.swift " ) } )
69+ XCTAssertTrue ( seen. contains { $0. path . hasSuffix ( " project/real1.swift " ) } )
70+ XCTAssertTrue ( seen. contains { $0. path . hasSuffix ( " project/real2.swift " ) } )
4671 // Hidden but found through the visible symlink project/link.swift
47- XCTAssertTrue ( seen. contains { $0. hasSuffix ( " project/.hidden.swift " ) } )
72+ XCTAssertTrue ( seen. contains { $0. path . hasSuffix ( " project/.hidden.swift " ) } )
4873 }
4974
5075 func testTraversesHiddenFilesIfExplicitlySpecified( ) throws {
@@ -56,8 +81,8 @@ final class FileIteratorTests: XCTestCase {
5681 followSymlinks: false
5782 )
5883 XCTAssertEqual ( seen. count, 2 )
59- XCTAssertTrue ( seen. contains { $0. hasSuffix ( " project/.build/generated.swift " ) } )
60- XCTAssertTrue ( seen. contains { $0. hasSuffix ( " project/.hidden.swift " ) } )
84+ XCTAssertTrue ( seen. contains { $0. path . hasSuffix ( " project/.build/generated.swift " ) } )
85+ XCTAssertTrue ( seen. contains { $0. path . hasSuffix ( " project/.hidden.swift " ) } )
6186 }
6287
6388 func testDoesNotFollowSymlinksIfFollowSymlinksIsFalseEvenIfExplicitlySpecified( ) {
@@ -71,6 +96,32 @@ final class FileIteratorTests: XCTestCase {
7196 )
7297 XCTAssertTrue ( seen. isEmpty)
7398 }
99+
100+ func testDoesNotTrimFirstCharacterOfPathIfRunningInRoot( ) throws {
101+ // Find the root of tmpdir. On Unix systems, this is always `/`. On Windows it is the drive.
102+ var root = tmpdir!
103+ while !root. isRoot {
104+ root. deleteLastPathComponent ( )
105+ }
106+ var rootPath = root. path
107+ #if os(Windows) && compiler(<6.1)
108+ if rootPath. hasPrefix ( " / " ) {
109+ // Canonicalize /C: to C:
110+ rootPath = String ( rootPath. dropFirst ( ) )
111+ }
112+ #endif
113+ // Make sure that we don't drop the beginning of the path if we are running in root.
114+ // https://github.com/swiftlang/swift-format/issues/862
115+ let seen = allFilesSeen ( iteratingOver: [ tmpdir] , followSymlinks: false , workingDirectory: root) . map ( \. relativePath)
116+ XCTAssertTrue ( seen. allSatisfy { $0. hasPrefix ( rootPath) } , " \( seen) does not contain root directory ' \( rootPath) ' " )
117+ }
118+
119+ func testShowsRelativePaths( ) throws {
120+ // Make sure that we still show the relative path if using them.
121+ // https://github.com/swiftlang/swift-format/issues/862
122+ let seen = allFilesSeen ( iteratingOver: [ tmpdir] , followSymlinks: false , workingDirectory: tmpdir)
123+ XCTAssertEqual ( Set ( seen. map ( \. relativePath) ) , [ " project/real1.swift " , " project/real2.swift " ] )
124+ }
74125}
75126
76127extension FileIteratorTests {
@@ -111,11 +162,15 @@ extension FileIteratorTests {
111162 }
112163
113164 /// Computes the list of all files seen by using `FileIterator` to iterate over the given URLs.
114- private func allFilesSeen( iteratingOver urls: [ URL ] , followSymlinks: Bool ) -> [ String ] {
115- let iterator = FileIterator ( urls: urls, followSymlinks: followSymlinks)
116- var seen : [ String ] = [ ]
165+ private func allFilesSeen(
166+ iteratingOver urls: [ URL ] ,
167+ followSymlinks: Bool ,
168+ workingDirectory: URL = URL ( fileURLWithPath: " . " )
169+ ) -> [ URL ] {
170+ let iterator = FileIterator ( urls: urls, followSymlinks: followSymlinks, workingDirectory: workingDirectory)
171+ var seen : [ URL ] = [ ]
117172 for next in iterator {
118- seen. append ( next. path )
173+ seen. append ( next)
119174 }
120175 return seen
121176 }
0 commit comments