@@ -23,6 +23,18 @@ import TestSupport
23
23
@testable import Foundation
24
24
#endif
25
25
26
+ private func checkBehavior< T: Equatable > ( _ result: T , new: T , old: T ) {
27
+ #if FOUNDATION_FRAMEWORK
28
+ if foundation_swift_url_enabled ( ) {
29
+ XCTAssertEqual ( result, new)
30
+ } else {
31
+ XCTAssertEqual ( result, old)
32
+ }
33
+ #else
34
+ XCTAssertEqual ( result, new)
35
+ #endif
36
+ }
37
+
26
38
final class URLTests : XCTestCase {
27
39
28
40
func testURLBasics( ) throws {
@@ -87,11 +99,7 @@ final class URLTests : XCTestCase {
87
99
XCTAssertEqual ( relativeURLWithBase. password ( ) , baseURL. password ( ) )
88
100
XCTAssertEqual ( relativeURLWithBase. host ( ) , baseURL. host ( ) )
89
101
XCTAssertEqual ( relativeURLWithBase. port, baseURL. port)
90
- #if !FOUNDATION_FRAMEWORK_NSURL
91
- XCTAssertEqual ( relativeURLWithBase. path ( ) , " /base/relative/path " )
92
- #else
93
- XCTAssertEqual ( relativeURLWithBase. path ( ) , " relative/path " )
94
- #endif
102
+ checkBehavior ( relativeURLWithBase. path ( ) , new: " /base/relative/path " , old: " relative/path " )
95
103
XCTAssertEqual ( relativeURLWithBase. relativePath, " relative/path " )
96
104
XCTAssertEqual ( relativeURLWithBase. query ( ) , " query " )
97
105
XCTAssertEqual ( relativeURLWithBase. fragment ( ) , " fragment " )
@@ -565,13 +573,8 @@ final class URLTests : XCTestCase {
565
573
// `appending(component:)` should explicitly treat `component` as a single
566
574
// path component, meaning "/" should be encoded to "%2F" before appending
567
575
appended = url. appending ( component: slashComponent, directoryHint: . notDirectory)
568
- #if FOUNDATION_FRAMEWORK_NSURL
569
- XCTAssertEqual ( appended. absoluteString, " file:///var/mobile/relative/with:slash " )
570
- XCTAssertEqual ( appended. relativePath, " relative/with:slash " )
571
- #else
572
- XCTAssertEqual ( appended. absoluteString, " file:///var/mobile/relative/%2Fwith:slash " )
573
- XCTAssertEqual ( appended. relativePath, " relative/%2Fwith:slash " )
574
- #endif
576
+ checkBehavior ( appended. absoluteString, new: " file:///var/mobile/relative/%2Fwith:slash " , old: " file:///var/mobile/relative/with:slash " )
577
+ checkBehavior ( appended. relativePath, new: " relative/%2Fwith:slash " , old: " relative/with:slash " )
575
578
576
579
appended = url. appendingPathComponent ( component, isDirectory: false )
577
580
XCTAssertEqual ( appended. absoluteString, " file:///var/mobile/relative/no:slash " )
@@ -685,12 +688,49 @@ final class URLTests : XCTestCase {
685
688
XCTAssertEqual ( url. path ( ) , " /path.foo/ " )
686
689
url. append ( path: " ///// " )
687
690
url. deletePathExtension ( )
688
- #if !FOUNDATION_FRAMEWORK_NSURL
689
- XCTAssertEqual ( url. path ( ) , " /path/ " )
690
- #else
691
691
// Old behavior only searches the last empty component, so the extension isn't actually removed
692
- XCTAssertEqual ( url. path ( ) , " /path.foo/// " )
693
- #endif
692
+ checkBehavior ( url. path ( ) , new: " /path/ " , old: " /path.foo/// " )
693
+ }
694
+
695
+ func testURLAppendingToEmptyPath( ) throws {
696
+ let baseURL = URL ( filePath: " /base/directory " , directoryHint: . isDirectory)
697
+ let emptyPathURL = URL ( filePath: " " , relativeTo: baseURL)
698
+ let url = emptyPathURL. appending ( path: " main.swift " )
699
+ // New behavior keeps the path relative without needing to insert "."
700
+ checkBehavior ( url. relativePath, new: " main.swift " , old: " ./main.swift " )
701
+ XCTAssertEqual ( url. path, " /base/directory/main.swift " )
702
+
703
+ var example = try XCTUnwrap ( URL ( string: " https://example.com " ) )
704
+ XCTAssertEqual ( example. host ( ) , " example.com " )
705
+ XCTAssertTrue ( example. path ( ) . isEmpty)
706
+
707
+ // Appending to an empty path should add a slash if an authority exists
708
+ // The appended path should never become part of the host
709
+ example. append ( path: " foo " )
710
+ XCTAssertEqual ( example. host ( ) , " example.com " )
711
+ XCTAssertEqual ( example. path ( ) , " /foo " )
712
+ XCTAssertEqual ( example. absoluteString, " https://example.com/foo " )
713
+
714
+ var emptyHost = try XCTUnwrap ( URL ( string: " scheme:// " ) )
715
+ XCTAssertTrue ( emptyHost. host ( ) ? . isEmpty ?? true )
716
+ XCTAssertTrue ( emptyHost. path ( ) . isEmpty)
717
+
718
+ emptyHost. append ( path: " foo " )
719
+ XCTAssertTrue ( emptyHost. host ( ) ? . isEmpty ?? true )
720
+ // Old behavior failed to append correctly to an empty host
721
+ // Modern parsers agree that "foo" relative to "scheme://" is "scheme:///foo"
722
+ checkBehavior ( emptyHost. path ( ) , new: " /foo " , old: " " )
723
+ checkBehavior ( emptyHost. absoluteString, new: " scheme:///foo " , old: " scheme:// " )
724
+
725
+ var schemeOnly = try XCTUnwrap ( URL ( string: " scheme: " ) )
726
+ XCTAssertTrue ( schemeOnly. host ( ) ? . isEmpty ?? true )
727
+ XCTAssertTrue ( schemeOnly. path ( ) . isEmpty)
728
+
729
+ schemeOnly. append ( path: " foo " )
730
+ XCTAssertTrue ( schemeOnly. host ( ) ? . isEmpty ?? true )
731
+ // Old behavior appends to the string, but is missing the path
732
+ checkBehavior ( schemeOnly. path ( ) , new: " foo " , old: " " )
733
+ XCTAssertEqual ( schemeOnly. absoluteString, " scheme:foo " )
694
734
}
695
735
696
736
func testURLComponentsPercentEncodedUnencodedProperties( ) throws {
0 commit comments