@@ -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 " )
@@ -583,6 +586,156 @@ final class URLTests : XCTestCase {
583
586
XCTAssertEqual ( appended. relativePath, " relative/with:slash " )
584
587
}
585
588
589
+ func testURLDeletingLastPathComponent( ) throws {
590
+ var absolute = URL ( filePath: " /absolute/path " , directoryHint: . notDirectory)
591
+ // Note: .relativePath strips the trailing slash for compatibility
592
+ XCTAssertEqual ( absolute. relativePath, " /absolute/path " )
593
+ XCTAssertFalse ( absolute. hasDirectoryPath)
594
+
595
+ absolute. deleteLastPathComponent ( )
596
+ XCTAssertEqual ( absolute. relativePath, " /absolute " )
597
+ XCTAssertTrue ( absolute. hasDirectoryPath)
598
+
599
+ absolute. deleteLastPathComponent ( )
600
+ XCTAssertEqual ( absolute. relativePath, " / " )
601
+ XCTAssertTrue ( absolute. hasDirectoryPath)
602
+
603
+ // The old .deleteLastPathComponent() implementation appends ".." to the
604
+ // root directory "/", resulting in "/../". This resolves back to "/".
605
+ // The new implementation simply leaves "/" as-is.
606
+ absolute. deleteLastPathComponent ( )
607
+ checkBehavior ( absolute. relativePath, new: " / " , old: " /.. " )
608
+ XCTAssertTrue ( absolute. hasDirectoryPath)
609
+
610
+ absolute. append ( path: " absolute " , directoryHint: . isDirectory)
611
+ checkBehavior ( absolute. path, new: " /absolute " , old: " /../absolute " )
612
+
613
+ // Reset `var absolute` to "/absolute" to prevent having
614
+ // a "/../" prefix in all the old expectations.
615
+ absolute = URL ( filePath: " /absolute " , directoryHint: . isDirectory)
616
+
617
+ var relative = URL ( filePath: " relative/path " , directoryHint: . notDirectory, relativeTo: absolute)
618
+ XCTAssertEqual ( relative. relativePath, " relative/path " )
619
+ XCTAssertFalse ( relative. hasDirectoryPath)
620
+ XCTAssertEqual ( relative. path, " /absolute/relative/path " )
621
+
622
+ relative. deleteLastPathComponent ( )
623
+ XCTAssertEqual ( relative. relativePath, " relative " )
624
+ XCTAssertTrue ( relative. hasDirectoryPath)
625
+ XCTAssertEqual ( relative. path, " /absolute/relative " )
626
+
627
+ relative. deleteLastPathComponent ( )
628
+ checkBehavior ( relative. relativePath, new: " " , old: " . " )
629
+ XCTAssertTrue ( relative. hasDirectoryPath)
630
+ XCTAssertEqual ( relative. path, " /absolute " )
631
+
632
+ relative. deleteLastPathComponent ( )
633
+ XCTAssertEqual ( relative. relativePath, " .. " )
634
+ XCTAssertTrue ( relative. hasDirectoryPath)
635
+ XCTAssertEqual ( relative. path, " / " )
636
+
637
+ relative. deleteLastPathComponent ( )
638
+ XCTAssertEqual ( relative. relativePath, " ../.. " )
639
+ XCTAssertTrue ( relative. hasDirectoryPath)
640
+ checkBehavior ( relative. path, new: " / " , old: " /.. " )
641
+
642
+ relative. append ( path: " path " , directoryHint: . isDirectory)
643
+ XCTAssertEqual ( relative. relativePath, " ../../path " )
644
+ XCTAssertTrue ( relative. hasDirectoryPath)
645
+ checkBehavior ( relative. path, new: " /path " , old: " /../path " )
646
+
647
+ relative. deleteLastPathComponent ( )
648
+ XCTAssertEqual ( relative. relativePath, " ../.. " )
649
+ XCTAssertTrue ( relative. hasDirectoryPath)
650
+ checkBehavior ( relative. path, new: " / " , old: " /.. " )
651
+
652
+ relative = URL ( filePath: " " , relativeTo: absolute)
653
+ checkBehavior ( relative. relativePath, new: " " , old: " . " )
654
+ XCTAssertTrue ( relative. hasDirectoryPath)
655
+ XCTAssertEqual ( relative. path, " /absolute " )
656
+
657
+ relative. deleteLastPathComponent ( )
658
+ checkBehavior ( relative. relativePath, new: " .. " , old: " .. " )
659
+ XCTAssertTrue ( relative. hasDirectoryPath)
660
+ XCTAssertEqual ( relative. path, " / " )
661
+
662
+ relative. deleteLastPathComponent ( )
663
+ checkBehavior ( relative. relativePath, new: " ../.. " , old: " ../.. " )
664
+ XCTAssertTrue ( relative. hasDirectoryPath)
665
+ checkBehavior ( relative. path, new: " / " , old: " /.. " )
666
+
667
+ relative = URL ( filePath: " relative/./ " , relativeTo: absolute)
668
+ // According to RFC 3986, "." and ".." segments should not be removed
669
+ // until the path is resolved against the base URL (when calling .path)
670
+ checkBehavior ( relative. relativePath, new: " relative/. " , old: " relative " )
671
+ XCTAssertTrue ( relative. hasDirectoryPath)
672
+ XCTAssertEqual ( relative. path, " /absolute/relative " )
673
+
674
+ relative. deleteLastPathComponent ( )
675
+ checkBehavior ( relative. relativePath, new: " relative/./.. " , old: " . " )
676
+ XCTAssertTrue ( relative. hasDirectoryPath)
677
+ XCTAssertEqual ( relative. path, " /absolute " )
678
+
679
+ relative = URL ( filePath: " relative/. " , directoryHint: . isDirectory, relativeTo: absolute)
680
+ checkBehavior ( relative. relativePath, new: " relative/. " , old: " relative " )
681
+ XCTAssertTrue ( relative. hasDirectoryPath)
682
+ XCTAssertEqual ( relative. path, " /absolute/relative " )
683
+
684
+ relative. deleteLastPathComponent ( )
685
+ checkBehavior ( relative. relativePath, new: " relative/./.. " , old: " . " )
686
+ XCTAssertTrue ( relative. hasDirectoryPath)
687
+ XCTAssertEqual ( relative. path, " /absolute " )
688
+
689
+ relative = URL ( filePath: " relative/.. " , relativeTo: absolute)
690
+ XCTAssertEqual ( relative. relativePath, " relative/.. " )
691
+ checkBehavior ( relative. hasDirectoryPath, new: true , old: false )
692
+ XCTAssertEqual ( relative. path, " /absolute " )
693
+
694
+ relative. deleteLastPathComponent ( )
695
+ XCTAssertEqual ( relative. relativePath, " relative/../.. " )
696
+ XCTAssertTrue ( relative. hasDirectoryPath)
697
+ XCTAssertEqual ( relative. path, " / " )
698
+
699
+ relative = URL ( filePath: " relative/.. " , directoryHint: . isDirectory, relativeTo: absolute)
700
+ XCTAssertEqual ( relative. relativePath, " relative/.. " )
701
+ XCTAssertTrue ( relative. hasDirectoryPath)
702
+ XCTAssertEqual ( relative. path, " /absolute " )
703
+
704
+ relative. deleteLastPathComponent ( )
705
+ XCTAssertEqual ( relative. relativePath, " relative/../.. " )
706
+ XCTAssertTrue ( relative. hasDirectoryPath)
707
+ XCTAssertEqual ( relative. path, " / " )
708
+
709
+ var url = try XCTUnwrap ( URL ( string: " scheme://host.with.no.path " ) )
710
+ XCTAssertTrue ( url. path ( ) . isEmpty)
711
+
712
+ url. deleteLastPathComponent ( )
713
+ XCTAssertEqual ( url. absoluteString, " scheme://host.with.no.path " )
714
+ XCTAssertTrue ( url. path ( ) . isEmpty)
715
+
716
+ let unusedBase = URL ( string: " base://url " )
717
+ url = try XCTUnwrap ( URL ( string: " scheme://host.with.no.path " , relativeTo: unusedBase) )
718
+ XCTAssertEqual ( url. absoluteString, " scheme://host.with.no.path " )
719
+ XCTAssertTrue ( url. path ( ) . isEmpty)
720
+
721
+ url. deleteLastPathComponent ( )
722
+ XCTAssertEqual ( url. absoluteString, " scheme://host.with.no.path " )
723
+ XCTAssertTrue ( url. path ( ) . isEmpty)
724
+
725
+ var schemeRelative = try XCTUnwrap ( URL ( string: " scheme:relative/path " ) )
726
+ // Bug in the old implementation where a relative path is not recognized
727
+ checkBehavior ( schemeRelative. relativePath, new: " relative/path " , old: " " )
728
+
729
+ schemeRelative. deleteLastPathComponent ( )
730
+ checkBehavior ( schemeRelative. relativePath, new: " relative " , old: " " )
731
+
732
+ schemeRelative. deleteLastPathComponent ( )
733
+ XCTAssertEqual ( schemeRelative. relativePath, " " )
734
+
735
+ schemeRelative. deleteLastPathComponent ( )
736
+ XCTAssertEqual ( schemeRelative. relativePath, " " )
737
+ }
738
+
586
739
func testURLFilePathDropsTrailingSlashes( ) throws {
587
740
var url = URL ( filePath: " /path/slashes/// " )
588
741
XCTAssertEqual ( url. path ( ) , " /path/slashes/// " )
@@ -685,12 +838,8 @@ final class URLTests : XCTestCase {
685
838
XCTAssertEqual ( url. path ( ) , " /path.foo/ " )
686
839
url. append ( path: " ///// " )
687
840
url. deletePathExtension ( )
688
- #if !FOUNDATION_FRAMEWORK_NSURL
689
- XCTAssertEqual ( url. path ( ) , " /path/ " )
690
- #else
691
841
// Old behavior only searches the last empty component, so the extension isn't actually removed
692
- XCTAssertEqual ( url. path ( ) , " /path.foo/// " )
693
- #endif
842
+ checkBehavior ( url. path ( ) , new: " /path/ " , old: " /path.foo/// " )
694
843
}
695
844
696
845
func testURLComponentsPercentEncodedUnencodedProperties( ) throws {
0 commit comments