1
+ // This source file is part of the Swift.org open source project
2
+ //
3
+ // Copyright (c) 2025 Apple Inc. and the Swift project authors
4
+ // Licensed under Apache License v2.0 with Runtime Library Exception
5
+ //
6
+ // See http://swift.org/LICENSE.txt for license information
7
+ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8
+ //
9
+
10
+ #if os(Windows)
11
+ // Import Windows C functions
12
+ import WinSDK
13
+
14
+ // Declare _NS_getcwd function for testing
15
+ @_silgen_name ( " _NS_getcwd " )
16
+ func _NS_getcwd( _ buffer: UnsafeMutablePointer < CChar > , _ size: Int ) -> UnsafeMutablePointer < CChar > ?
17
+ #endif
18
+
19
+ class TestCFPlatformGetcwd : XCTestCase {
20
+
21
+ #if os(Windows)
22
+ func test_NS_getcwd_UNC_prefix_stripping( ) {
23
+ // Test that _NS_getcwd properly strips UNC long path prefixes using PathCchStripPrefix
24
+
25
+ // Create a temporary directory to work with
26
+ let fm = FileManager . default
27
+ let tempDir = fm. temporaryDirectory. appendingPathComponent ( " test_getcwd_ \( UUID ( ) . uuidString) " )
28
+
29
+ do {
30
+ try fm. createDirectory ( at: tempDir, withIntermediateDirectories: true )
31
+ defer { try ? fm. removeItem ( at: tempDir) }
32
+
33
+ // Get original directory for restoration
34
+ var originalBuffer = [ CChar] ( repeating: 0 , count: Int ( MAX_PATH) )
35
+ guard _NS_getcwd ( & originalBuffer, originalBuffer. count) != nil else {
36
+ XCTFail ( " Failed to get original directory " )
37
+ return
38
+ }
39
+ let originalDir = String ( cString: originalBuffer)
40
+
41
+ defer {
42
+ // Restore original directory
43
+ originalDir. withCString { _chdir ( $0) }
44
+ }
45
+
46
+ // Test with UNC long path prefix \\?\
47
+ let uncLongPathPrefix = " \\ \\ ? \\ " + tempDir. path
48
+ let uncLongPathCString = uncLongPathPrefix. cString ( using: . utf8) !
49
+ let uncChdirResult = uncLongPathCString. withUnsafeBufferPointer { buffer in
50
+ return _chdir ( buffer. baseAddress!)
51
+ }
52
+ XCTAssertEqual ( uncChdirResult, 0 , " Failed to change directory using UNC long path prefix " )
53
+
54
+ // Test _NS_getcwd directly after changing to UNC prefixed path
55
+ var buffer = [ CChar] ( repeating: 0 , count: Int ( MAX_PATH) )
56
+ guard let result = _NS_getcwd ( & buffer, buffer. count) else {
57
+ XCTFail ( " _NS_getcwd returned null " )
58
+ return
59
+ }
60
+
61
+ let currentDir = String ( cString: result)
62
+
63
+ // Verify that the path doesn't contain UNC prefixes (this is the key test!)
64
+ XCTAssertFalse ( currentDir. hasPrefix ( " \\ \\ ? \\ " ) , " Current directory path should not contain \\ \\ ? \\ UNC prefix after stripping " )
65
+
66
+ // Verify that we can still access the directory (it's a valid path)
67
+ XCTAssertTrue ( fm. fileExists ( atPath: currentDir) , " Current directory path should be valid and accessible " )
68
+
69
+ // Verify the path ends with our test directory name
70
+ XCTAssertTrue ( currentDir. hasSuffix ( tempDir. lastPathComponent) , " Current directory should end with our test directory name " )
71
+
72
+ // Test with a deeper nested directory using UNC prefix to ensure stripping works with longer paths
73
+ let deepDir = tempDir. appendingPathComponent ( " level1 " ) . appendingPathComponent ( " level2 " ) . appendingPathComponent ( " level3 " )
74
+ try fm. createDirectory ( at: deepDir, withIntermediateDirectories: true )
75
+
76
+ let deepUncPath = " \\ \\ ? \\ " + deepDir. path
77
+ let deepUncCString = deepUncPath. cString ( using: . utf8) !
78
+ let deepChdirResult = deepUncCString. withUnsafeBufferPointer { buffer in
79
+ return _chdir ( buffer. baseAddress!)
80
+ }
81
+ XCTAssertEqual ( deepChdirResult, 0 , " Failed to change to deep directory with UNC prefix " )
82
+
83
+ // Test _NS_getcwd with deep UNC prefixed path
84
+ var deepBuffer = [ CChar] ( repeating: 0 , count: Int ( MAX_PATH) )
85
+ guard let deepResult = _NS_getcwd ( & deepBuffer, deepBuffer. count) else {
86
+ XCTFail ( " _NS_getcwd returned null for deep UNC path " )
87
+ return
88
+ }
89
+
90
+ let deepCurrentDir = String ( cString: deepResult)
91
+
92
+ // Verify UNC prefixes are stripped from deeper paths too
93
+ XCTAssertFalse ( deepCurrentDir. hasPrefix ( " \\ \\ ? \\ " ) , " Deep directory path should not contain \\ \\ ? \\ UNC prefix after stripping " )
94
+ XCTAssertTrue ( fm. fileExists ( atPath: deepCurrentDir) , " Deep directory path should be valid and accessible " )
95
+ XCTAssertTrue ( deepCurrentDir. hasSuffix ( " level3 " ) , " Deep directory should end with level3 " )
96
+
97
+ } catch {
98
+ XCTFail ( " Failed to set up test environment: \( error) " )
99
+ }
100
+ }
101
+
102
+ func test_NS_getcwd_small_buffer( ) {
103
+ // Test that _NS_getcwd handles small buffer correctly
104
+ var smallBuffer = [ CChar] ( repeating: 0 , count: 1 )
105
+ let result = _NS_getcwd ( & smallBuffer, smallBuffer. count)
106
+ // This should either return null or handle the small buffer gracefully
107
+ // The exact behavior depends on the implementation, but it shouldn't crash
108
+ XCTAssertTrue ( result == nil || result != nil , " Function should not crash with small buffer " )
109
+ }
110
+ #endif
111
+ }
0 commit comments