@@ -23,6 +23,18 @@ import TestSupport
2323@testable import Foundation
2424#endif
2525
26+ private func checkBehavior< T: Equatable > ( _ result: T , new: T , old: T , file: StaticString = #filePath, line: UInt = #line) {
27+ #if FOUNDATION_FRAMEWORK
28+ if foundation_swift_url_enabled ( ) {
29+ XCTAssertEqual ( result, new, file: file, line: line)
30+ } else {
31+ XCTAssertEqual ( result, old, file: file, line: line)
32+ }
33+ #else
34+ XCTAssertEqual ( result, new, file: file, line: line)
35+ #endif
36+ }
37+
2638final class URLTests : XCTestCase {
2739
2840 func testURLBasics( ) throws {
@@ -87,11 +99,7 @@ final class URLTests : XCTestCase {
8799 XCTAssertEqual ( relativeURLWithBase. password ( ) , baseURL. password ( ) )
88100 XCTAssertEqual ( relativeURLWithBase. host ( ) , baseURL. host ( ) )
89101 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 " )
95103 XCTAssertEqual ( relativeURLWithBase. relativePath, " relative/path " )
96104 XCTAssertEqual ( relativeURLWithBase. query ( ) , " query " )
97105 XCTAssertEqual ( relativeURLWithBase. fragment ( ) , " fragment " )
@@ -154,7 +162,7 @@ final class URLTests : XCTestCase {
154162 " http:g " : " http:g " , // For strict parsers
155163 ]
156164
157- #if FOUNDATION_FRAMEWORK_NSURL
165+ #if FOUNDATION_FRAMEWORK
158166 let testsFailingWithoutSwiftURL = Set ( [
159167 " " ,
160168 " ../../../g " ,
@@ -165,8 +173,8 @@ final class URLTests : XCTestCase {
165173 #endif
166174
167175 for test in tests {
168- #if FOUNDATION_FRAMEWORK_NSURL
169- if testsFailingWithoutSwiftURL. contains ( test. key) {
176+ #if FOUNDATION_FRAMEWORK
177+ if !foundation_swift_url_enabled ( ) , testsFailingWithoutSwiftURL. contains ( test. key) {
170178 continue
171179 }
172180 #endif
@@ -178,8 +186,8 @@ final class URLTests : XCTestCase {
178186 }
179187
180188 func testURLPathAPIsResolveAgainstBase( ) throws {
181- #if FOUNDATION_FRAMEWORK_NSURL
182- try XCTSkipIf ( true )
189+ #if FOUNDATION_FRAMEWORK
190+ try XCTSkipIf ( !foundation_swift_url_enabled ( ) )
183191 #endif
184192 // Borrowing the same test cases from RFC 3986, but checking paths
185193 let base = URL ( string: " http://a/b/c/d;p?q " )
@@ -246,8 +254,8 @@ final class URLTests : XCTestCase {
246254 }
247255
248256 func testURLPathComponentsPercentEncodedSlash( ) throws {
249- #if FOUNDATION_FRAMEWORK_NSURL
250- try XCTSkipIf ( true )
257+ #if FOUNDATION_FRAMEWORK
258+ try XCTSkipIf ( !foundation_swift_url_enabled ( ) )
251259 #endif
252260
253261 var url = try XCTUnwrap ( URL ( string: " https://example.com/https%3A%2F%2Fexample.com " ) )
@@ -270,8 +278,8 @@ final class URLTests : XCTestCase {
270278 }
271279
272280 func testURLRootlessPath( ) throws {
273- #if FOUNDATION_FRAMEWORK_NSURL
274- try XCTSkipIf ( true )
281+ #if FOUNDATION_FRAMEWORK
282+ try XCTSkipIf ( !foundation_swift_url_enabled ( ) )
275283 #endif
276284
277285 let paths = [ " " , " path " ]
@@ -565,13 +573,8 @@ final class URLTests : XCTestCase {
565573 // `appending(component:)` should explicitly treat `component` as a single
566574 // path component, meaning "/" should be encoded to "%2F" before appending
567575 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 " )
575578
576579 appended = url. appendingPathComponent ( component, isDirectory: false )
577580 XCTAssertEqual ( appended. absoluteString, " file:///var/mobile/relative/no:slash " )
@@ -677,7 +680,48 @@ final class URLTests : XCTestCase {
677680 XCTAssertEqual ( url. path ( ) , " /path.foo/ " )
678681 url. append ( path: " ///// " )
679682 url. deletePathExtension ( )
680- XCTAssertEqual ( url. path ( ) , " /path/ " )
683+ // Old behavior only searches the last empty component, so the extension isn't actually removed
684+ checkBehavior ( url. path ( ) , new: " /path/ " , old: " /path.foo/// " )
685+ }
686+
687+ func testURLAppendingToEmptyPath( ) throws {
688+ let baseURL = URL ( filePath: " /base/directory " , directoryHint: . isDirectory)
689+ let emptyPathURL = URL ( filePath: " " , relativeTo: baseURL)
690+ let url = emptyPathURL. appending ( path: " main.swift " )
691+ XCTAssertEqual ( url. relativePath, " ./main.swift " )
692+ XCTAssertEqual ( url. path, " /base/directory/main.swift " )
693+
694+ var example = try XCTUnwrap ( URL ( string: " https://example.com " ) )
695+ XCTAssertEqual ( example. host ( ) , " example.com " )
696+ XCTAssertTrue ( example. path ( ) . isEmpty)
697+
698+ // Appending to an empty path should add a slash if an authority exists
699+ // The appended path should never become part of the host
700+ example. append ( path: " foo " )
701+ XCTAssertEqual ( example. host ( ) , " example.com " )
702+ XCTAssertEqual ( example. path ( ) , " /foo " )
703+ XCTAssertEqual ( example. absoluteString, " https://example.com/foo " )
704+
705+ var emptyHost = try XCTUnwrap ( URL ( string: " scheme:// " ) )
706+ XCTAssertTrue ( emptyHost. host ( ) ? . isEmpty ?? true )
707+ XCTAssertTrue ( emptyHost. path ( ) . isEmpty)
708+
709+ emptyHost. append ( path: " foo " )
710+ XCTAssertTrue ( emptyHost. host ( ) ? . isEmpty ?? true )
711+ // Old behavior failed to append correctly to an empty host
712+ // Modern parsers agree that "foo" relative to "scheme://" is "scheme:///foo"
713+ checkBehavior ( emptyHost. path ( ) , new: " /foo " , old: " " )
714+ checkBehavior ( emptyHost. absoluteString, new: " scheme:///foo " , old: " scheme:// " )
715+
716+ var schemeOnly = try XCTUnwrap ( URL ( string: " scheme: " ) )
717+ XCTAssertTrue ( schemeOnly. host ( ) ? . isEmpty ?? true )
718+ XCTAssertTrue ( schemeOnly. path ( ) . isEmpty)
719+
720+ schemeOnly. append ( path: " foo " )
721+ XCTAssertTrue ( schemeOnly. host ( ) ? . isEmpty ?? true )
722+ // Old behavior appends to the string, but is missing the path
723+ checkBehavior ( schemeOnly. path ( ) , new: " foo " , old: " " )
724+ XCTAssertEqual ( schemeOnly. absoluteString, " scheme:foo " )
681725 }
682726
683727 func testURLComponentsPercentEncodedUnencodedProperties( ) throws {
0 commit comments