@@ -3770,14 +3770,6 @@ extension KeyValuePairsTests {
37703770 )
37713771 }
37723772
3773- func testAssignment( ) {
3774- // FIXME: Test assigning a KeyValuePairs instance from another URL.
3775- }
3776- }
3777-
3778-
3779- extension KeyValuePairsTests {
3780-
37813773 /// Tests using a key-value string schema with ASCII alpha delimiters.
37823774 ///
37833775 /// While not recommended, in theory it should work.
@@ -3812,75 +3804,117 @@ extension KeyValuePairsTests {
38123804 }
38133805}
38143806
3807+ extension KeyValuePairsTests {
3808+
3809+ /// Tests assigning a KeyValuePairs view from one URL to another.
3810+ ///
3811+ /// This is not generally allowed, but there are a handful of situations where it works.
3812+ /// Unfortunately we can't test the situations where it doesn't work, because they trigger a fatal error
3813+ /// and XCTest doesn't support testing such things.
3814+ ///
3815+ func testAssignment( ) {
3816+
3817+ // Straightforward 'queryParams' assignment.
3818+ // This works because 'queryParams' includes a 'set' accessor, in addition to 'modify'.
38153819
3820+ do {
3821+ let srcQuery = " test=src&foo=bar&specials= \( Self . SpecialCharacters_Escaped_PrctEnc_Query) "
38163822
3823+ let src = WebURL ( " http://src/? \( srcQuery) " ) !
3824+ var dst = WebURL ( " file://dst/ " ) !
3825+ XCTAssertEqual ( src. queryParams. count, 3 )
3826+ XCTAssertEqual ( dst. queryParams. count, 0 )
38173827
3828+ dst. queryParams = src. queryParams
38183829
3830+ XCTAssertEqual ( src. serialized ( ) , " http://src/? \( srcQuery) " )
3831+ XCTAssertEqual ( dst. serialized ( ) , " file://dst/? \( srcQuery) " )
3832+ XCTAssertEqual ( src. queryParams. count, 3 )
3833+ XCTAssertEqual ( dst. queryParams. count, 3 )
3834+ }
38193835
3836+ // Assigning different views originating from the same storage.
3837+ // This works basically as a side-effect of how the mutating scope ID is calculated 😕,
3838+ // but it's also not _really_ a problem because data outside the viewed component cannot be changed.
38203839
3840+ do {
3841+ var url1 = WebURL ( " file://xyz/ " ) !
3842+ var url2 = url1
3843+
3844+ url2. withMutableKeyValuePairs ( in: . query, schema: . formEncoded) { kvps2 in
3845+ url1. withMutableKeyValuePairs ( in: . query, schema: . formEncoded) { kvps1 in
3846+ kvps2 += [ ( " hello " , " world " ) ]
3847+ kvps1 = kvps2
3848+ kvps2 += [ ( " after " , " wards " ) ]
3849+ }
3850+ }
3851+
3852+ XCTAssertEqual ( url1. serialized ( ) , " file://xyz/?hello=world " )
3853+ XCTAssertEqual ( url2. serialized ( ) , " file://xyz/?hello=world&after=wards " )
3854+ }
38213855
3856+ // These should all cause runtime traps.
3857+ // Unfortunately we can't test that with XCTest :(
38223858
3859+ #if false
38233860
3861+ let check = 0
38243862
3863+ // Reassignment via modify accessor. Different source URLs.
38253864
3826- // func testAssignment() {
3827- //
3828- // do {
3829- // var url0 = WebURL("http://example.com?a=b&c+is the key=d&&e=&=foo&e=g&e&h=👀&e=f")!
3830- // XCTAssertEqual(url0.serialized(), "http://example.com/?a=b&c+is%20the%20key=d&&e=&=foo&e=g&e&h=%F0%9F%91%80&e=f")
3831- // XCTAssertEqual(url0.query, "a=b&c+is%20the%20key=d&&e=&=foo&e=g&e&h=%F0%9F%91%80&e=f")
3832- // XCTAssertFalse(url0.storage.structure.queryIsKnownFormEncoded)
3833- //
3834- // var url1 = WebURL("foo://bar")!
3835- // XCTAssertEqual(url1.serialized(), "foo://bar")
3836- // XCTAssertNil(url1.query)
3837- // XCTAssertTrue(url1.storage.structure.queryIsKnownFormEncoded)
3838- //
3839- // // Set url1's formParams from empty to url0's non-empty formParams.
3840- // // url1's query string should be the form-encoded version version of url0's query, which itself remains unchanged.
3841- // url1.queryParams = url0.queryParams
3842- // XCTAssertEqual(url1.serialized(), "foo://bar?a=b&c+is%20the%20key=d&&e=&=foo&e=g&e&h=%F0%9F%91%80&e=f")
3843- // XCTAssertEqual(url1.query, "a=b&c+is%20the%20key=d&&e=&=foo&e=g&e&h=%F0%9F%91%80&e=f")
3844- // XCTAssertEqual(url0.serialized(), "http://example.com/?a=b&c+is%20the%20key=d&&e=&=foo&e=g&e&h=%F0%9F%91%80&e=f")
3845- // XCTAssertEqual(url0.query, "a=b&c+is%20the%20key=d&&e=&=foo&e=g&e&h=%F0%9F%91%80&e=f")
3846- // XCTAssertFalse(url0.storage.structure.queryIsKnownFormEncoded)
3847- // XCTAssertFalse(url1.storage.structure.queryIsKnownFormEncoded)
3848- // XCTAssertURLIsIdempotent(url1)
3849- //
3850- // // Reset url1 to a nil query. Set url0's non-empty params to url1's empty params.
3851- // // url0 should now have a nil query, and url1 remains unchanged.
3852- // url1 = WebURL("foo://bar")!
3853- // XCTAssertEqual(url1.serialized(), "foo://bar")
3854- // XCTAssertNil(url1.query)
3855- // XCTAssertTrue(url1.storage.structure.queryIsKnownFormEncoded)
3856- //
3857- // url0.queryParams = url1.queryParams
3858- // XCTAssertEqual(url0.serialized(), "http://example.com/")
3859- // XCTAssertNil(url0.query)
3860- // XCTAssertTrue(url0.storage.structure.queryIsKnownFormEncoded)
3861- // XCTAssertEqual(url1.serialized(), "foo://bar")
3862- // XCTAssertNil(url1.query)
3863- // XCTAssertTrue(url1.storage.structure.queryIsKnownFormEncoded)
3864- // XCTAssertURLIsIdempotent(url0)
3865- // }
3866- //
3867- // // Assigning a URL's query parameters to itself has no effect.
3868- // do {
3869- // var url = WebURL("http://example.com?a=b&c+is the key=d&&e=&=foo&e=g&e&h=👀&e=f&&&")!
3870- // XCTAssertEqual(
3871- // url.serialized(), "http://example.com/?a=b&c+is%20the%20key=d&&e=&=foo&e=g&e&h=%F0%9F%91%80&e=f&&&"
3872- // )
3873- // XCTAssertEqual(url.query, "a=b&c+is%20the%20key=d&&e=&=foo&e=g&e&h=%F0%9F%91%80&e=f&&&")
3874- // XCTAssertFalse(url.storage.structure.queryIsKnownFormEncoded)
3875- //
3876- // url.queryParams = url.queryParams
3877- // XCTAssertEqual(
3878- // url.serialized(), "http://example.com/?a=b&c+is%20the%20key=d&&e=&=foo&e=g&e&h=%F0%9F%91%80&e=f&&&"
3879- // )
3880- // XCTAssertEqual(url.query, "a=b&c+is%20the%20key=d&&e=&=foo&e=g&e&h=%F0%9F%91%80&e=f&&&")
3881- // XCTAssertFalse(url.storage.structure.queryIsKnownFormEncoded)
3882- // XCTAssertURLIsIdempotent(url)
3883- // }
3884- // }
3885- //
3886- //}
3865+ if check == 0 {
3866+ let src = WebURL ( " http://src/ " ) !
3867+ var dst = WebURL ( " file://dst/ " ) !
3868+
3869+ @inline ( never)
3870+ func assign< T> ( _ x: inout T , to y: T ) {
3871+ x = y
3872+ }
3873+
3874+ assign ( & dst. queryParams, to: src. queryParams)
3875+ XCTFail ( " Should have trapped " )
3876+ }
3877+
3878+ // Reassignment via modify accessor. Same source URLs, different components.
3879+
3880+ if check == 1 {
3881+ let url1 = WebURL ( " http://xyz/ " ) !
3882+ var url2 = url1
3883+
3884+ @inline ( never)
3885+ func assign< T> ( _ x: inout T , to y: T ) {
3886+ x = y
3887+ }
3888+
3889+ assign ( & url2. queryParams, to: url1. keyValuePairs ( in: . fragment, schema: . formEncoded) )
3890+ XCTFail ( " Should have trapped " )
3891+ }
3892+
3893+ // Reassignment via scoped method. Different source URLs.
3894+
3895+ if check == 2 {
3896+ let src = WebURL ( " http://src/ " ) !
3897+ var dst = WebURL ( " file://dst/ " ) !
3898+
3899+ dst. withMutableKeyValuePairs ( in: . query, schema: . formEncoded) { $0 = src. queryParams }
3900+ XCTFail ( " Should have trapped " )
3901+ }
3902+
3903+ // Reassignment via scoped method. Same source URLs, different components.
3904+
3905+ if check == 3 {
3906+ var url1 = WebURL ( " file://xyz/ " ) !
3907+ var url2 = url1
3908+
3909+ url1. withMutableKeyValuePairs ( in: . query, schema: . formEncoded) { kvps1 in
3910+ url2. withMutableKeyValuePairs ( in: . fragment, schema: . formEncoded) { kvps2 in
3911+ kvps2 += [ ( " hello " , " world " ) ]
3912+ kvps1 = kvps2
3913+ }
3914+ }
3915+ XCTFail ( " Should have trapped " )
3916+ }
3917+
3918+ #endif
3919+ }
3920+ }
0 commit comments