Skip to content

Commit 1ca142e

Browse files
authored
[Foundation] Correct NSRange equality typo and add unit tests for newly added NSRange behaviors (#10282)
1 parent 3548c45 commit 1ca142e

File tree

2 files changed

+197
-2
lines changed

2 files changed

+197
-2
lines changed

stdlib/public/SDK/Foundation/NSRange.swift

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,23 @@ extension NSRange : Hashable {
2222
}
2323

2424
public static func==(_ lhs: NSRange, _ rhs: NSRange) -> Bool {
25-
return lhs.location == rhs.location && rhs.length == rhs.length
25+
return lhs.location == rhs.location && lhs.length == rhs.length
2626
}
2727
}
2828

2929
extension NSRange : CustomStringConvertible, CustomDebugStringConvertible {
3030
public var description: String { return "{\(location), \(length)}" }
31-
public var debugDescription: String { return "{\(location), \(length)}" }
31+
public var debugDescription: String {
32+
guard location != NSNotFound else {
33+
return "{NSNotFound, \(length)}"
34+
}
35+
return "{\(location), \(length)}"
36+
}
3237
}
3338

3439
extension NSRange {
3540
public init?(_ string: String) {
41+
var savedLocation = 0
3642
if string.isEmpty {
3743
// fail early if the string is empty
3844
return nil
@@ -45,23 +51,52 @@ extension NSRange {
4551
return nil
4652
}
4753
var location = 0
54+
savedLocation = scanner.scanLocation
4855
guard scanner.scanInt(&location) else {
4956
return nil
5057
}
5158
if scanner.isAtEnd {
5259
// return early if there are no more characters after the first int in the string
5360
return nil
5461
}
62+
if scanner.scanString(".", into: nil) {
63+
scanner.scanLocation = savedLocation
64+
var double = 0.0
65+
guard scanner.scanDouble(&double) else {
66+
return nil
67+
}
68+
guard let integral = Int(exactly: double) else {
69+
return nil
70+
}
71+
location = integral
72+
}
73+
5574
let _ = scanner.scanUpToCharacters(from: digitSet, into: nil)
5675
if scanner.isAtEnd {
5776
// return early if there are no integer characters after the first int in the string
5877
return nil
5978
}
6079
var length = 0
80+
savedLocation = scanner.scanLocation
6181
guard scanner.scanInt(&length) else {
6282
return nil
6383
}
6484

85+
if !scanner.isAtEnd {
86+
if scanner.scanString(".", into: nil) {
87+
scanner.scanLocation = savedLocation
88+
var double = 0.0
89+
guard scanner.scanDouble(&double) else {
90+
return nil
91+
}
92+
guard let integral = Int(exactly: double) else {
93+
return nil
94+
}
95+
length = integral
96+
}
97+
}
98+
99+
65100
self.location = location
66101
self.length = length
67102
}

test/stdlib/TestNSRange.swift

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
2+
// Licensed under Apache License v2.0 with Runtime Library Exception
3+
//
4+
// See https://swift.org/LICENSE.txt for license information
5+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// RUN: %target-run-simple-swift
10+
// REQUIRES: executable_test
11+
// REQUIRES: objc_interop
12+
13+
import Foundation
14+
15+
#if FOUNDATION_XCTEST
16+
import XCTest
17+
class TestNSRangeSuper : XCTestCase { }
18+
#else
19+
import StdlibUnittest
20+
class TestNSRangeSuper { }
21+
#endif
22+
23+
class TestNSRange : TestNSRangeSuper {
24+
func testEquality() {
25+
let r1 = NSRange(location: 1, length: 10)
26+
let r2 = NSRange(location: 1, length: 11)
27+
let r3 = NSRange(location: 2, length: 10)
28+
let r4 = NSRange(location: 1, length: 10)
29+
let r5 = NSRange(location: NSNotFound, length: 0)
30+
let r6 = NSRange(location: NSNotFound, length: 2)
31+
32+
expectNotEqual(r1, r2)
33+
expectNotEqual(r1, r3)
34+
expectEqual(r1, r4)
35+
expectNotEqual(r1, r5)
36+
expectNotEqual(r5, r6)
37+
}
38+
39+
func testDescription() {
40+
let r1 = NSRange(location: 0, length: 22)
41+
let r2 = NSRange(location: 10, length: 22)
42+
let r3 = NSRange(location: NSNotFound, length: 0)
43+
let r4 = NSRange(location: NSNotFound, length: 22)
44+
expectEqual("{0, 22}", r1.description)
45+
expectEqual("{10, 22}", r2.description)
46+
expectEqual("{\(NSNotFound), 0}", r3.description)
47+
expectEqual("{\(NSNotFound), 22}", r4.description)
48+
49+
expectEqual("{0, 22}", r1.debugDescription)
50+
expectEqual("{10, 22}", r2.debugDescription)
51+
expectEqual("{NSNotFound, 0}", r3.debugDescription)
52+
expectEqual("{NSNotFound, 22}", r4.debugDescription)
53+
}
54+
55+
func testCreationFromString() {
56+
let r1 = NSRange("")
57+
expectNil(r1)
58+
let r2 = NSRange("1")
59+
expectNil(r2)
60+
let r3 = NSRange("1 2")
61+
expectEqual(NSRange(location: 1, length: 2), r3)
62+
let r4 = NSRange("{1 8")
63+
expectEqual(NSRange(location: 1, length: 8), r4)
64+
let r5 = NSRange("1.8")
65+
expectNil(r5)
66+
let r6 = NSRange("1-9")
67+
expectEqual(NSRange(location: 1, length: 9), r6)
68+
let r7 = NSRange("{1,9}")
69+
expectEqual(NSRange(location: 1, length: 9), r7)
70+
let r8 = NSRange("{1,9}asdfasdf")
71+
expectEqual(NSRange(location: 1, length: 9), r8)
72+
let r9 = NSRange("{1,9}{2,7}")
73+
expectEqual(NSRange(location: 1, length: 9), r9)
74+
let r10 = NSRange("{1,9}")
75+
expectEqual(NSRange(location: 1, length: 9), r10)
76+
let r11 = NSRange("{1.0,9}")
77+
expectEqual(NSRange(location: 1, length: 9), r11)
78+
let r12 = NSRange("{1,9.0}")
79+
expectEqual(NSRange(location: 1, length: 9), r12)
80+
let r13 = NSRange("{1.2,9}")
81+
expectNil(r13)
82+
let r14 = NSRange("{1,9.8}")
83+
expectNil(r14)
84+
}
85+
86+
func testHashing() {
87+
let r1 = NSRange(location: 10, length: 22)
88+
let r2 = NSRange(location: 10, length: 22)
89+
let r3 = NSRange(location: 1, length: 22)
90+
expectEqual(r1.hashValue, r2.hashValue)
91+
expectNotEqual(r1.hashValue, r3.hashValue)
92+
let rangeSet: Set<NSRange> = [r1, r2, r3]
93+
expectEqual(2, rangeSet.count)
94+
}
95+
96+
func testBounding() {
97+
let r1 = NSRange(location: 1000, length: 2222)
98+
expectEqual(r1.location, r1.lowerBound)
99+
expectEqual(r1.location + r1.length, r1.upperBound)
100+
}
101+
102+
func testContains() {
103+
let r1 = NSRange(location: 1000, length: 2222)
104+
expectFalse(r1.contains(3))
105+
expectTrue(r1.contains(1001))
106+
expectFalse(r1.contains(4000))
107+
}
108+
109+
func testUnion() {
110+
let r1 = NSRange(location: 10, length: 20)
111+
let r2 = NSRange(location: 30, length: 5)
112+
let union1 = r1.union(r2)
113+
114+
expectEqual(Swift.min(r1.lowerBound, r2.lowerBound), union1.lowerBound)
115+
expectEqual(Swift.max(r1.upperBound, r2.upperBound), union1.upperBound)
116+
117+
let r3 = NSRange(location: 10, length: 20)
118+
let r4 = NSRange(location: 11, length: 5)
119+
let union2 = r3.union(r4)
120+
121+
expectEqual(Swift.min(r3.lowerBound, r4.lowerBound), union2.lowerBound)
122+
expectEqual(Swift.max(r3.upperBound, r4.upperBound), union2.upperBound)
123+
124+
let r5 = NSRange(location: 10, length: 20)
125+
let r6 = NSRange(location: 11, length: 29)
126+
let union3 = r5.union(r6)
127+
128+
expectEqual(Swift.min(r5.lowerBound, r6.upperBound), union3.lowerBound)
129+
expectEqual(Swift.max(r5.upperBound, r6.upperBound), union3.upperBound)
130+
}
131+
132+
func testIntersection() {
133+
let r1 = NSRange(location: 1, length: 7)
134+
let r2 = NSRange(location: 2, length: 20)
135+
let r3 = NSRange(location: 2, length: 2)
136+
let r4 = NSRange(location: 10, length: 7)
137+
138+
let intersection1 = r1.intersection(r2)
139+
expectEqual(NSRange(location: 2, length: 6), intersection1)
140+
let intersection2 = r1.intersection(r3)
141+
expectEqual(NSRange(location: 2, length: 2), intersection2)
142+
let intersection3 = r1.intersection(r4)
143+
expectEqual(nil, intersection3)
144+
}
145+
}
146+
147+
#if !FOUNDATION_XCTEST
148+
var NSRangeTests = TestSuite("TestNSRange")
149+
150+
NSRangeTests.test("testEquality") { TestNSRange().testEquality() }
151+
NSRangeTests.test("testDescription") { TestNSRange().testDescription() }
152+
NSRangeTests.test("testCreationFromString") { TestNSRange().testCreationFromString() }
153+
NSRangeTests.test("testHashing") { TestNSRange().testHashing() }
154+
NSRangeTests.test("testBounding") { TestNSRange().testBounding() }
155+
NSRangeTests.test("testContains") { TestNSRange().testContains() }
156+
NSRangeTests.test("testUnion") { TestNSRange().testUnion() }
157+
NSRangeTests.test("testIntersection") { TestNSRange().testIntersection() }
158+
159+
runAllTests()
160+
#endif

0 commit comments

Comments
 (0)