Skip to content

Commit ecd595a

Browse files
committed
Merge pull request #209 from czechboy0/hd/tests/string_utils
[Tests] Refactored parsing of scheme from repo URL, added Collection tests
2 parents 46e221b + bb98759 commit ecd595a

File tree

8 files changed

+312
-165
lines changed

8 files changed

+312
-165
lines changed

Sources/Utility/ArrayExtensions.swift

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright 2015 - 2016 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
extension Collection {
12+
13+
public func pick(body: (Iterator.Element) -> Bool) -> Iterator.Element? {
14+
for x in self where body(x) {
15+
return x
16+
}
17+
return nil
18+
}
19+
20+
public func partition<T, U>() -> ([T], [U]) {
21+
var t = [T]()
22+
var u = [U]()
23+
for e in self {
24+
if let e = e as? T {
25+
t.append(e)
26+
} else if let e = e as? U {
27+
u.append(e)
28+
}
29+
}
30+
return (t, u)
31+
}
32+
33+
public func partition(body: (Iterator.Element) -> Bool) -> ([Iterator.Element], [Iterator.Element]) {
34+
var a: [Iterator.Element] = []
35+
var b: [Iterator.Element] = []
36+
for e in self {
37+
if body(e) {
38+
a.append(e)
39+
} else {
40+
b.append(e)
41+
}
42+
}
43+
return (a, b)
44+
}
45+
}
46+
47+
extension Collection where Iterator.Element : Equatable {
48+
49+
/// Split around a delimiting subsequence with maximum number of splits == 2
50+
func splitAround(delimiter: [Iterator.Element]) -> ([Iterator.Element], [Iterator.Element]?) {
51+
52+
let orig = Array(self)
53+
let end = orig.endIndex
54+
let delimCount = delimiter.count
55+
56+
var index = orig.startIndex
57+
while index+delimCount <= end {
58+
let cur = Array(orig[index..<index+delimCount])
59+
if cur == delimiter {
60+
//found
61+
let leading = Array(orig[0..<index])
62+
let trailing = Array(orig.suffix(orig.count-leading.count-delimCount))
63+
return (leading, trailing)
64+
} else {
65+
//not found, move index down
66+
index = index.successor()
67+
}
68+
}
69+
return (orig, nil)
70+
}
71+
}

Sources/Utility/StringExtensions.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,16 @@ extension String {
7070

7171
return String(cc)
7272
}
73+
74+
/// Splits string around a delimiter string into up to two substrings
75+
/// If delimiter is not found, the second returned substring is nil
76+
func splitAround(delimiter: String) -> (String, String?) {
77+
let comps = self.characters.splitAround(Array(delimiter.characters))
78+
let head = String(comps.0)
79+
if let tail = comps.1 {
80+
return (head, String(tail))
81+
} else {
82+
return (head, nil)
83+
}
84+
}
7385
}

Sources/Utility/URL.swift

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,30 @@
99
*/
1010

1111
public struct URL {
12+
13+
/// Parses the URL type of a git repository
14+
/// e.g. https://github.com/apple/swift returns "https"
15+
/// e.g. [email protected]:apple/swift returns "git"
16+
///
17+
/// This is *not* a generic URI scheme parser!
1218
public static func scheme(url: String) -> String? {
13-
// this is not fully RFC compliant, so it either has to be
14-
// or we need to use CFLite FIXME
1519

16-
let count = url.characters.count
17-
18-
func foo(start: Int) -> String? {
19-
guard count > start + 3 else { return nil }
20-
21-
let a = url.startIndex
22-
let b = a.advanced(by: start)
23-
let c = b.advanced(by: 3)
24-
if url[b..<c] == "://" || url[b] == "@" {
25-
return url[a..<b]
26-
} else {
20+
func prefixOfSplitBy(delimiter: String) -> String? {
21+
let (head, tail) = url.splitAround(delimiter)
22+
if tail == nil {
23+
//not found
2724
return nil
2825
}
26+
//found, return head
27+
//lowercase the "scheme", as specified by the URI RFC (just in case)
28+
return head.lowercased()
29+
}
30+
31+
for delim in ["://", "@"] {
32+
if let found = prefixOfSplitBy(delim) {
33+
return found
34+
}
2935
}
30-
return foo(4) ?? foo(5) ?? foo(3)
36+
return nil
3137
}
3238
}

Tests/Utility/CollectionTests.swift

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright 2015 - 2016 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
@testable import Utility
12+
import XCTest
13+
14+
class CollectionTests: XCTestCase {
15+
16+
func testPick() {
17+
18+
let body = { (num: Int) -> Bool in num > 5 }
19+
20+
XCTAssertNil([].pick(body))
21+
XCTAssertNil([3, 4].pick(body))
22+
XCTAssertEqual([3, 7].pick(body), 7)
23+
XCTAssertEqual([3, 8, 7].pick(body), 8)
24+
}
25+
26+
func testPartitionByType() {
27+
28+
let input0: [Any] = []
29+
let output0: ([String], [Int]) = input0.partition()
30+
XCTAssertEqual(output0.0, [String]())
31+
XCTAssertEqual(output0.1, [Int]())
32+
33+
let input1: [Any] = [1, "two", 3, "four"]
34+
let output1: ([String], [Int]) = input1.partition()
35+
XCTAssertEqual(output1.0, ["two", "four"])
36+
XCTAssertEqual(output1.1, [1, 3])
37+
}
38+
39+
func testPartitionByClosure() {
40+
41+
func eq(lhs: ([Int], [Int]), _ rhs: ([Int], [Int]), file: StaticString = #file, line: UInt = #line) {
42+
XCTAssertEqual(lhs.0, rhs.0, file: file, line: line)
43+
XCTAssertEqual(lhs.1, rhs.1, file: file, line: line)
44+
}
45+
46+
let body = { (num: Int) -> Bool in num > 5 }
47+
48+
eq([Int]().partition(body), ([], []))
49+
eq([2].partition(body), ([], [2]))
50+
eq([7].partition(body), ([7], []))
51+
eq([7, 4, 2, 9].partition(body), ([7, 9], [4, 2]))
52+
}
53+
54+
func testSplitAround() {
55+
56+
func eq(lhs: ([Character], [Character]?), _ rhs: ([Character], [Character]?), file: StaticString = #file, line: UInt = #line) {
57+
XCTAssertEqual(lhs.0, rhs.0, file: file, line: line)
58+
XCTAssertEqual(lhs.1 ?? [], rhs.1 ?? [], file: file, line: line)
59+
}
60+
61+
eq([].splitAround([":"]), ([], nil))
62+
eq(["f", "o", "o"].splitAround([":"]), (["f", "o", "o"], nil))
63+
eq(["f", "o", "o", ":"].splitAround([":"]), (["f", "o", "o"], []))
64+
eq([":", "b", "a", "r"].splitAround([":"]), ([], ["b", "a", "r"]))
65+
eq(["f", "o", "o", ":", "b", "a", "r"].splitAround([":"]), (["f", "o", "o"], ["b", "a", "r"]))
66+
67+
}
68+
}

Tests/Utility/FileTests.swift

Lines changed: 0 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -67,103 +67,3 @@ class FileTests: XCTestCase {
6767
}
6868
}
6969
}
70-
71-
72-
extension FileTests {
73-
static var allTests : [(String, FileTests -> () throws -> Void)] {
74-
return [
75-
("testOpenFile", testOpenFile),
76-
("testOpenFileFail", testOpenFileFail),
77-
("testReadRegularTextFile", testReadRegularTextFile),
78-
("testReadRegularTextFileWithSeparator", testReadRegularTextFileWithSeparator)
79-
]
80-
}
81-
}
82-
83-
84-
extension RmtreeTests {
85-
static var allTests : [(String, RmtreeTests -> () throws -> Void)] {
86-
return [
87-
("testDoesNotFollowSymlinks", testDoesNotFollowSymlinks),
88-
]
89-
}
90-
}
91-
92-
extension PathTests {
93-
static var allTests : [(String, PathTests -> () throws -> Void)] {
94-
return [
95-
("test", test),
96-
("testPrecombined", testPrecombined),
97-
("testExtraSeparators", testExtraSeparators),
98-
("testEmpties", testEmpties),
99-
("testNormalizePath", testNormalizePath),
100-
("testJoinWithAbsoluteReturnsLastAbsoluteComponent", testJoinWithAbsoluteReturnsLastAbsoluteComponent),
101-
("testParentDirectory", testParentDirectory),
102-
]
103-
}
104-
}
105-
106-
extension WalkTests {
107-
static var allTests : [(String, WalkTests -> () throws -> Void)] {
108-
return [
109-
("testNonRecursive", testNonRecursive),
110-
("testRecursive", testRecursive),
111-
("testSymlinksNotWalked", testSymlinksNotWalked),
112-
("testWalkingADirectorySymlinkResolvesOnce", testWalkingADirectorySymlinkResolvesOnce),
113-
]
114-
}
115-
}
116-
117-
extension StatTests {
118-
static var allTests : [(String, StatTests -> () throws -> Void)] {
119-
return [
120-
("test_isdir", test_isdir),
121-
("test_isfile", test_isfile),
122-
("test_realpath", test_realpath),
123-
("test_basename", test_basename),
124-
]
125-
}
126-
}
127-
128-
extension RelativePathTests {
129-
static var allTests : [(String, RelativePathTests -> () throws -> Void)] {
130-
return [
131-
("testAbsolute", testAbsolute),
132-
("testRelative", testRelative),
133-
("testMixed", testMixed),
134-
("testRelativeCommonSubprefix", testRelativeCommonSubprefix),
135-
("testCombiningRelativePaths", testCombiningRelativePaths)
136-
]
137-
}
138-
}
139-
140-
extension ShellTests {
141-
static var allTests : [(String, ShellTests -> () throws -> Void)] {
142-
return [
143-
("testPopen", testPopen),
144-
("testPopenWithBufferLargerThanThatAllocated", testPopenWithBufferLargerThanThatAllocated),
145-
("testPopenWithBinaryOutput", testPopenWithBinaryOutput)
146-
]
147-
}
148-
}
149-
150-
151-
extension StringTests {
152-
static var allTests : [(String, StringTests -> () throws -> Void)] {
153-
return [
154-
("testTrailingChomp", testTrailingChomp),
155-
("testEmptyChomp", testEmptyChomp),
156-
("testSeparatorChomp", testSeparatorChomp),
157-
("testChuzzle", testChuzzle),
158-
]
159-
}
160-
161-
}
162-
163-
extension URLTests {
164-
static var allTests : [(String, URLTests -> () throws -> Void)] {
165-
return [
166-
("testSchema", testSchema),
167-
]
168-
}
169-
}

Tests/Utility/StringTests.swift

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,31 @@ class StringTests: XCTestCase {
4242
XCTAssertEqual(" a\t\r\n".chuzzle(), "a")
4343
XCTAssertEqual("b".chuzzle(), "b")
4444
}
45+
46+
func testSplitAround() {
47+
48+
func eq(lhs: (String, String?), _ rhs: (String, String?), file: StaticString = #file, line: UInt = #line) {
49+
XCTAssertEqual(lhs.0, rhs.0, file: file, line: line)
50+
XCTAssertEqual(lhs.1, rhs.1, file: file, line: line)
51+
}
52+
53+
eq("".splitAround("::"), ("", nil))
54+
eq("foo".splitAround("::"), ("foo", nil))
55+
eq("foo::".splitAround("::"), ("foo", ""))
56+
eq("::bar".splitAround("::"), ("", "bar"))
57+
eq("foo::bar".splitAround("::"), ("foo", "bar"))
58+
}
4559
}
4660

47-
4861
class URLTests: XCTestCase {
4962

5063
func testSchema() {
51-
let a = "http://github.com/foo/bar"
52-
let b = "https://github.com/foo/bar"
53-
let c = "[email protected]/foo/bar"
54-
XCTAssertEqual(Utility.URL.scheme(a), "http")
55-
XCTAssertEqual(Utility.URL.scheme(b), "https")
56-
XCTAssertEqual(Utility.URL.scheme(c), "git")
64+
XCTAssertEqual(Utility.URL.scheme("http://github.com/foo/bar"), "http")
65+
XCTAssertEqual(Utility.URL.scheme("https://github.com/foo/bar"), "https")
66+
XCTAssertEqual(Utility.URL.scheme("HTTPS://github.com/foo/bar"), "https")
67+
XCTAssertEqual(Utility.URL.scheme("[email protected]/foo/bar"), "git")
68+
XCTAssertEqual(Utility.URL.scheme("[email protected]/foo/bar"), "ssh")
69+
XCTAssertNil(Utility.URL.scheme("github.com/foo/bar"))
70+
XCTAssertNil(Utility.URL.scheme("user:/github.com/foo/bar"))
5771
}
5872
}

0 commit comments

Comments
 (0)