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