1
- @_spi ( Internal) import SwiftFormat
1
+ @_spi ( Internal) @ _spi ( Testing ) import SwiftFormat
2
2
import XCTest
3
3
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
+
4
29
final class FileIteratorTests : XCTestCase {
5
30
private var tmpdir : URL !
6
31
@@ -10,7 +35,7 @@ final class FileIteratorTests: XCTestCase {
10
35
in: . userDomainMask,
11
36
appropriateFor: FileManager . default. temporaryDirectory,
12
37
create: true
13
- )
38
+ ) . realpath
14
39
15
40
// Create a simple file tree used by the tests below.
16
41
try touch ( " project/real1.swift " )
@@ -31,8 +56,8 @@ final class FileIteratorTests: XCTestCase {
31
56
#endif
32
57
let seen = allFilesSeen ( iteratingOver: [ tmpdir] , followSymlinks: false )
33
58
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 " ) } )
36
61
}
37
62
38
63
func testFollowSymlinks( ) throws {
@@ -41,10 +66,10 @@ final class FileIteratorTests: XCTestCase {
41
66
#endif
42
67
let seen = allFilesSeen ( iteratingOver: [ tmpdir] , followSymlinks: true )
43
68
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 " ) } )
46
71
// 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 " ) } )
48
73
}
49
74
50
75
func testTraversesHiddenFilesIfExplicitlySpecified( ) throws {
@@ -56,8 +81,8 @@ final class FileIteratorTests: XCTestCase {
56
81
followSymlinks: false
57
82
)
58
83
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 " ) } )
61
86
}
62
87
63
88
func testDoesNotFollowSymlinksIfFollowSymlinksIsFalseEvenIfExplicitlySpecified( ) {
@@ -71,6 +96,32 @@ final class FileIteratorTests: XCTestCase {
71
96
)
72
97
XCTAssertTrue ( seen. isEmpty)
73
98
}
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
+ }
74
125
}
75
126
76
127
extension FileIteratorTests {
@@ -111,11 +162,15 @@ extension FileIteratorTests {
111
162
}
112
163
113
164
/// 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 ] = [ ]
117
172
for next in iterator {
118
- seen. append ( next. path )
173
+ seen. append ( next)
119
174
}
120
175
return seen
121
176
}
0 commit comments