Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Sources/Shared/Toolkit/URL/Absolute URL/FileURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ public struct FileURL: AbsoluteURL, Hashable, Sendable {
public func isDirectory() throws -> Bool {
try (url.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) ?? false
}

public func hash(into hasher: inout Hasher) {
hasher.combine(path)
hasher.combine(url.user)
}

public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.path == rhs.path
&& lhs.url.user == rhs.url.user
}
}

public extension URLConvertible {
Expand Down
16 changes: 16 additions & 0 deletions Sources/Shared/Toolkit/URL/Absolute URL/HTTPURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ public struct HTTPURL: AbsoluteURL, Hashable, Sendable {
}
return o
}

public func hash(into hasher: inout Hasher) {
hasher.combine(origin)
hasher.combine(path)
hasher.combine(query)
hasher.combine(fragment)
hasher.combine(url.user)
}

public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.origin == rhs.origin
&& lhs.path == rhs.path
&& lhs.query == rhs.query
&& lhs.fragment == rhs.fragment
&& lhs.url.user == rhs.url.user
}
}

public extension URLConvertible {
Expand Down
20 changes: 20 additions & 0 deletions Sources/Shared/Toolkit/URL/Absolute URL/UnknownAbsoluteURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,24 @@ struct UnknownAbsoluteURL: AbsoluteURL, Hashable {
let url: URL
let scheme: URLScheme
let origin: String? = nil

public func hash(into hasher: inout Hasher) {
hasher.combine(scheme)
hasher.combine(host)
hasher.combine(url.port)
hasher.combine(path)
hasher.combine(query)
hasher.combine(fragment)
hasher.combine(url.user)
}

public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.scheme == rhs.scheme
&& lhs.host == rhs.host
&& lhs.url.port == rhs.url.port
&& lhs.path == rhs.path
&& lhs.query == rhs.query
&& lhs.fragment == rhs.fragment
&& lhs.url.user == rhs.url.user
}
}
12 changes: 12 additions & 0 deletions Sources/Shared/Toolkit/URL/RelativeURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ public struct RelativeURL: URLProtocol, Hashable {
.removingPrefix("/")
)
}

public func hash(into hasher: inout Hasher) {
hasher.combine(path)
hasher.combine(query)
hasher.combine(fragment)
}

public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.path == rhs.path
&& lhs.query == rhs.query
&& lhs.fragment == rhs.fragment
}
}

/// Implements `URLConvertible`.
Expand Down
56 changes: 47 additions & 9 deletions Tests/SharedTests/Toolkit/URL/Absolute URL/FileURLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,60 @@ import XCTest

class FileURLTests: XCTestCase {
func testEquality() {
// Paths must be equal.
XCTAssertEqual(
FileURL(string: "file:///foo/bar")!,
FileURL(string: "file:///foo/bar")!
FileURL(string: "file:///foo/bar")
)
// Fragments are ignored.
XCTAssertNotEqual(
FileURL(string: "file:///foo/baz")!,
FileURL(string: "file:///foo/bar")
)

// Paths is compared percent and entity-decoded.
XCTAssertEqual(
FileURL(string: "file:///foo/bar")!,
FileURL(string: "file:///foo/bar#fragment")!
FileURL(string: "file:///c%27est%20valide")!,
FileURL(string: "file:///c%27est%20valide")
)
XCTAssertNotEqual(
FileURL(string: "file:///foo/bar")!,
FileURL(string: "file:///foo/baz")!
XCTAssertEqual(
FileURL(string: "file:///c'est%20valide")!,
FileURL(string: "file:///c%27est%20valide")
)

// Authority must be equal.
XCTAssertEqual(
FileURL(string: "file://user:password@host/foo")!,
FileURL(string: "file://user:password@host/foo")
)
XCTAssertNotEqual(
FileURL(string: "file:///foo/bar")!,
FileURL(string: "file:///foo/bar/")!
FileURL(string: "file://foo"),
FileURL(string: "file://host/foo")
)

// Query parameters are ignored.
XCTAssertEqual(
FileURL(string: "file:///foo/bar?b=b&a=a")!,
FileURL(string: "file:///foo/bar?a=a&b=b")
)
XCTAssertEqual(
FileURL(string: "file:///foo/bar?b=b")!,
FileURL(string: "file:///foo/bar?a=a")
)

// Scheme is case insensitive.
XCTAssertEqual(
FileURL(string: "FILE:///foo")!,
FileURL(string: "file:///foo")
)

// Fragment is ignored.
XCTAssertEqual(
FileURL(string: "file:///foo")!,
FileURL(string: "file:///foo#fragment")
)
XCTAssertEqual(
FileURL(string: "file:///foo#other")!,
FileURL(string: "file:///foo#fragment")
)
}

Expand Down
85 changes: 81 additions & 4 deletions Tests/SharedTests/Toolkit/URL/Absolute URL/HTTPURLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,90 @@ import XCTest

class HTTPURLTests: XCTestCase {
func testEquality() {
// Paths must be equal.
XCTAssertEqual(
HTTPURL(string: "http://domain.com")!,
HTTPURL(string: "http://domain.com")!
HTTPURL(string: "http://example.com/foo/bar")!,
HTTPURL(string: "http://example.com/foo/bar")
)
XCTAssertNotEqual(
HTTPURL(string: "http://domain.com")!,
HTTPURL(string: "http://domain.com#fragment")!
HTTPURL(string: "http://example.com/foo/baz")!,
HTTPURL(string: "http://example.com/foo/bar")
)

// Paths is compared percent and entity-decoded.
XCTAssertEqual(
HTTPURL(string: "http://example.com/c%27est%20valide")!,
HTTPURL(string: "http://example.com/c%27est%20valide")
)
XCTAssertEqual(
HTTPURL(string: "http://example.com/c'est%20valide")!,
HTTPURL(string: "http://example.com/c%27est%20valide")
)

// Authority must be equal.
XCTAssertEqual(
HTTPURL(string: "http://example.com/foo")!,
HTTPURL(string: "http://example.com/foo")
)
XCTAssertNotEqual(
HTTPURL(string: "http://example.com:80/foo")!,
HTTPURL(string: "http://example.com/foo")
)
XCTAssertNotEqual(
HTTPURL(string: "http://example.com:80/foo")!,
HTTPURL(string: "http://example.com:443/foo")
)
XCTAssertNotEqual(
HTTPURL(string: "http://example.com:80/foo")!,
HTTPURL(string: "http://example.com/foo")
)
XCTAssertNotEqual(
HTTPURL(string: "http://domain.com/foo")!,
HTTPURL(string: "http://example.com/foo")
)
XCTAssertNotEqual(
HTTPURL(string: "http://user:[email protected]/foo")!,
HTTPURL(string: "http://example.com/foo")
)
XCTAssertNotEqual(
HTTPURL(string: "http://user:[email protected]/foo")!,
HTTPURL(string: "http://other:[email protected]/foo")
)

// Order of query parameters is important.
XCTAssertNotEqual(
HTTPURL(string: "http://example.com/foo/bar?b=b&a=a")!,
HTTPURL(string: "http://example.com/foo/bar?a=a&b=b")
)

// Content of parameters is important.
XCTAssertEqual(
HTTPURL(string: "http://example.com/foo/bar?a=a&b=b")!,
HTTPURL(string: "http://example.com/foo/bar?a=a&b=b")
)
XCTAssertNotEqual(
HTTPURL(string: "http://example.com/foo/bar?b=b")!,
HTTPURL(string: "http://example.com/foo/bar?a=a")
)

// Scheme is case insensitive.
XCTAssertEqual(
HTTPURL(string: "HTTP://example.com/foo")!,
HTTPURL(string: "http://example.com/foo")
)
XCTAssertNotEqual(
HTTPURL(string: "https://example.com/foo")!,
HTTPURL(string: "http://example.com/foo")
)

// Fragment is relevant.
XCTAssertEqual(
HTTPURL(string: "http://example.com/foo#fragment")!,
HTTPURL(string: "http://example.com/foo#fragment")
)
XCTAssertNotEqual(
HTTPURL(string: "http://example.com/foo#other")!,
HTTPURL(string: "http://example.com/foo#fragment")
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,86 @@ import XCTest

class UnknownAbsoluteURLTests: XCTestCase {
func testEquality() {
// Paths must be equal.
XCTAssertEqual(
UnknownAbsoluteURL(string: "opds://domain.com")!,
UnknownAbsoluteURL(string: "opds://domain.com")!
UnknownAbsoluteURL(string: "opds://example.com/foo/bar")!,
UnknownAbsoluteURL(string: "opds://example.com/foo/bar")
)
XCTAssertNotEqual(
UnknownAbsoluteURL(string: "opds://domain.com")!,
UnknownAbsoluteURL(string: "opds://domain.com#fragment")!
UnknownAbsoluteURL(string: "opds://example.com/foo/baz")!,
UnknownAbsoluteURL(string: "opds://example.com/foo/bar")
)

// Paths is compared percent and entity-decoded.
XCTAssertEqual(
UnknownAbsoluteURL(string: "opds://example.com/c%27est%20valide")!,
UnknownAbsoluteURL(string: "opds://example.com/c%27est%20valide")
)
XCTAssertEqual(
UnknownAbsoluteURL(string: "opds://example.com/c'est%20valide")!,
UnknownAbsoluteURL(string: "opds://example.com/c%27est%20valide")
)

// Authority must be equal.
XCTAssertEqual(
UnknownAbsoluteURL(string: "opds://example.com/foo")!,
UnknownAbsoluteURL(string: "opds://example.com/foo")
)
XCTAssertNotEqual(
UnknownAbsoluteURL(string: "opds://example.com:80/foo")!,
UnknownAbsoluteURL(string: "opds://example.com/foo")
)
XCTAssertNotEqual(
UnknownAbsoluteURL(string: "opds://example.com:80/foo")!,
UnknownAbsoluteURL(string: "opds://example.com:443/foo")
)
XCTAssertNotEqual(
UnknownAbsoluteURL(string: "opds://example.com:80/foo")!,
UnknownAbsoluteURL(string: "opds://example.com/foo")
)
XCTAssertNotEqual(
UnknownAbsoluteURL(string: "opds://domain.com/foo")!,
UnknownAbsoluteURL(string: "opds://example.com/foo")
)
XCTAssertNotEqual(
UnknownAbsoluteURL(string: "opds://user:[email protected]/foo")!,
UnknownAbsoluteURL(string: "opds://example.com/foo")
)
XCTAssertNotEqual(
UnknownAbsoluteURL(string: "opds://user:[email protected]/foo")!,
UnknownAbsoluteURL(string: "opds://other:[email protected]/foo")
)

// Order of query parameters is important.
XCTAssertNotEqual(
UnknownAbsoluteURL(string: "opds://example.com/foo/bar?b=b&a=a")!,
UnknownAbsoluteURL(string: "opds://example.com/foo/bar?a=a&b=b")
)

// Content of parameters is important.
XCTAssertEqual(
UnknownAbsoluteURL(string: "opds://example.com/foo/bar?a=a&b=b")!,
UnknownAbsoluteURL(string: "opds://example.com/foo/bar?a=a&b=b")
)
XCTAssertNotEqual(
UnknownAbsoluteURL(string: "opds://example.com/foo/bar?b=b")!,
UnknownAbsoluteURL(string: "opds://example.com/foo/bar?a=a")
)

// Scheme is case insensitive.
XCTAssertEqual(
UnknownAbsoluteURL(string: "OPDS://example.com/foo")!,
UnknownAbsoluteURL(string: "opds://example.com/foo")
)

// Fragment is relevant.
XCTAssertEqual(
UnknownAbsoluteURL(string: "opds://example.com/foo#fragment")!,
UnknownAbsoluteURL(string: "opds://example.com/foo#fragment")
)
XCTAssertNotEqual(
UnknownAbsoluteURL(string: "opds://example.com/foo#other")!,
UnknownAbsoluteURL(string: "opds://example.com/foo#fragment")
)
}

Expand Down
31 changes: 19 additions & 12 deletions Tests/SharedTests/Toolkit/URL/RelativeURLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,25 @@ import XCTest

class RelativeURLTests: XCTestCase {
func testEquality() {
XCTAssertEqual(
RelativeURL(string: "dir/file")!,
RelativeURL(string: "dir/file")!
)
XCTAssertNotEqual(
RelativeURL(string: "dir/file/")!,
RelativeURL(string: "dir/file")!
)
XCTAssertNotEqual(
RelativeURL(string: "dir")!,
RelativeURL(string: "dir/file")!
)
// Paths must be equal.
XCTAssertEqual(RelativeURL(string: "foo/bar")!, RelativeURL(string: "foo/bar"))
XCTAssertNotEqual(RelativeURL(string: "foo/bar")!, RelativeURL(string: "foo/bar/"))
XCTAssertNotEqual(RelativeURL(string: "foo/baz")!, RelativeURL(string: "foo/bar"))

// Paths is compared percent and entity-decoded.
XCTAssertEqual(RelativeURL(string: "c%27est%20valide")!, RelativeURL(string: "c%27est%20valide"))
XCTAssertEqual(RelativeURL(string: "c'est%20valide")!, RelativeURL(string: "c%27est%20valide"))

// Order of query parameters is important.
XCTAssertNotEqual(RelativeURL(string: "foo/bar?b=b&a=a")!, RelativeURL(string: "foo/bar?a=a&b=b"))

// Content of parameters is important.
XCTAssertEqual(RelativeURL(string: "foo/bar?a=a&b=b")!, RelativeURL(string: "foo/bar?a=a&b=b"))
XCTAssertNotEqual(RelativeURL(string: "foo/bar?b=b")!, RelativeURL(string: "foo/bar?a=a"))

// Fragment is relevant.
XCTAssertEqual(RelativeURL(string: "foo/bar#fragment")!, RelativeURL(string: "foo/bar#fragment"))
XCTAssertNotEqual(RelativeURL(string: "foo/bar#other")!, RelativeURL(string: "foo/bar#fragment"))
}

// MARK: - URLProtocol
Expand Down