@@ -26,6 +26,8 @@ class TestNSAttributedString : XCTestCase {
26
26
( " test_initWithString " , test_initWithString) ,
27
27
( " test_initWithStringAndAttributes " , test_initWithStringAndAttributes) ,
28
28
( " test_longestEffectiveRange " , test_longestEffectiveRange) ,
29
+ ( " test_enumerateAttributeWithName " , test_enumerateAttributeWithName) ,
30
+ ( " test_enumerateAttributes " , test_enumerateAttributes) ,
29
31
]
30
32
}
31
33
@@ -37,14 +39,14 @@ class TestNSAttributedString : XCTestCase {
37
39
38
40
var range = NSRange ( )
39
41
let attrs = attrString. attributes ( at: 0 , effectiveRange: & range)
40
- XCTAssertEqual ( range. location, NSNotFound )
41
- XCTAssertEqual ( range. length, 0 )
42
+ XCTAssertEqual ( range. location, 0 )
43
+ XCTAssertEqual ( range. length, string . utf16 . count )
42
44
XCTAssertEqual ( attrs. count, 0 )
43
45
44
46
let attribute = attrString. attribute ( " invalid " , at: 0 , effectiveRange: & range)
45
47
XCTAssertNil ( attribute)
46
- XCTAssertEqual ( range. location, NSNotFound )
47
- XCTAssertEqual ( range. length, 0 )
48
+ XCTAssertEqual ( range. location, 0 )
49
+ XCTAssertEqual ( range. length, string . utf16 . count )
48
50
}
49
51
50
52
func test_initWithStringAndAttributes( ) {
@@ -67,8 +69,8 @@ class TestNSAttributedString : XCTestCase {
67
69
68
70
let invalidAttribute = attrString. attribute ( " invalid " , at: 0 , effectiveRange: & range)
69
71
XCTAssertNil ( invalidAttribute)
70
- XCTAssertEqual ( range. location, NSNotFound )
71
- XCTAssertEqual ( range. length, 0 )
72
+ XCTAssertEqual ( range. location, 0 )
73
+ XCTAssertEqual ( range. length, string . utf16 . count )
72
74
73
75
let attribute = attrString. attribute ( " attribute.placeholder.key " , at: 0 , effectiveRange: & range)
74
76
XCTAssertEqual ( range. location, 0 )
@@ -105,6 +107,136 @@ class TestNSAttributedString : XCTestCase {
105
107
XCTAssertEqual ( range. length, 28 )
106
108
}
107
109
110
+ func test_enumerateAttributeWithName( ) {
111
+ let string = " Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur et sem vitae consectetur. Nam venenatis lectus a laoreet blandit. "
112
+
113
+ let attrKey1 = " attribute.placeholder.key1 "
114
+ let attrValue1 = " attribute.placeholder.value1 "
115
+ let attrRange1 = NSRange ( location: 0 , length: 20 )
116
+ let attrRange2 = NSRange ( location: 18 , length: 10 )
117
+
118
+ let attrKey3 = " attribute.placeholder.key3 "
119
+ let attrValue3 = " attribute.placeholder.value3 "
120
+ let attrRange3 = NSRange ( location: 40 , length: 5 )
121
+
122
+ let attrString = NSMutableAttributedString ( string: string)
123
+ attrString. addAttribute ( attrKey1, value: attrValue1, range: attrRange1)
124
+ attrString. addAttribute ( attrKey1, value: attrValue1, range: attrRange2)
125
+ attrString. addAttribute ( attrKey3, value: attrValue3, range: attrRange3)
126
+
127
+ let fullRange = NSRange ( location: 0 , length: attrString. length)
128
+
129
+ var rangeDescriptionString = " "
130
+ var attrDescriptionString = " "
131
+ attrString. enumerateAttribute ( attrKey1, in: fullRange) { attr, range, stop in
132
+ rangeDescriptionString. append ( self . describe ( range: range) )
133
+ attrDescriptionString. append ( self . describe ( attr: attr) )
134
+ }
135
+ XCTAssertEqual ( rangeDescriptionString, " (0,28)(28,116) " )
136
+ XCTAssertEqual ( attrDescriptionString, " \( attrValue1) |nil| " )
137
+
138
+ rangeDescriptionString = " "
139
+ attrDescriptionString = " "
140
+ attrString. enumerateAttribute ( attrKey1, in: fullRange, options: [ . reverse] ) { attr, range, stop in
141
+ rangeDescriptionString. append ( self . describe ( range: range) )
142
+ attrDescriptionString. append ( self . describe ( attr: attr) )
143
+ }
144
+ XCTAssertEqual ( rangeDescriptionString, " (28,116)(0,28) " )
145
+ XCTAssertEqual ( attrDescriptionString, " nil| \( attrValue1) | " )
146
+
147
+ rangeDescriptionString = " "
148
+ attrDescriptionString = " "
149
+ attrString. enumerateAttribute ( attrKey1, in: fullRange, options: [ . longestEffectiveRangeNotRequired] ) { attr, range, stop in
150
+ rangeDescriptionString. append ( self . describe ( range: range) )
151
+ attrDescriptionString. append ( self . describe ( attr: attr) )
152
+ }
153
+ XCTAssertEqual ( rangeDescriptionString, " (0,28)(28,12)(40,5)(45,99) " )
154
+ XCTAssertEqual ( attrDescriptionString, " \( attrValue1) |nil|nil|nil| " )
155
+ }
156
+
157
+ func test_enumerateAttributes( ) {
158
+ let string = " Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur et sem vitae consectetur. Nam venenatis lectus a laoreet blandit. "
159
+
160
+ let attrKey1 = " attribute.placeholder.key1 "
161
+ let attrValue1 = " attribute.placeholder.value1 "
162
+ let attrRange1 = NSRange ( location: 0 , length: 20 )
163
+
164
+ let attrKey2 = " attribute.placeholder.key2 "
165
+ let attrValue2 = " attribute.placeholder.value2 "
166
+ let attrRange2 = NSRange ( location: 18 , length: 10 )
167
+
168
+ let attrKey3 = " attribute.placeholder.key3 "
169
+ let attrValue3 = " attribute.placeholder.value3 "
170
+ let attrRange3 = NSRange ( location: 40 , length: 5 )
171
+
172
+ let attrString = NSMutableAttributedString ( string: string)
173
+ attrString. addAttribute ( attrKey1, value: attrValue1, range: attrRange1)
174
+ attrString. addAttribute ( attrKey2, value: attrValue2, range: attrRange2)
175
+ attrString. addAttribute ( attrKey3, value: attrValue3, range: attrRange3)
176
+
177
+ let fullRange = NSRange ( location: 0 , length: attrString. length)
178
+
179
+ var rangeDescriptionString = " "
180
+ var attrsDescriptionString = " "
181
+ attrString. enumerateAttributes ( in: fullRange) { attrs, range, stop in
182
+ rangeDescriptionString. append ( self . describe ( range: range) )
183
+ attrsDescriptionString. append ( self . describe ( attrs: attrs) )
184
+ }
185
+ XCTAssertEqual ( rangeDescriptionString, " (0,18)(18,2)(20,8)(28,12)(40,5)(45,99) " )
186
+ XCTAssertEqual ( attrsDescriptionString, " [attribute.placeholder.key1:attribute.placeholder.value1][attribute.placeholder.key1:attribute.placeholder.value1,attribute.placeholder.key2:attribute.placeholder.value2][attribute.placeholder.key2:attribute.placeholder.value2][:][attribute.placeholder.key3:attribute.placeholder.value3][:] " )
187
+
188
+ rangeDescriptionString = " "
189
+ attrsDescriptionString = " "
190
+ attrString. enumerateAttributes ( in: fullRange, options: [ . reverse] ) { attrs, range, stop in
191
+ rangeDescriptionString. append ( self . describe ( range: range) )
192
+ attrsDescriptionString. append ( self . describe ( attrs: attrs) )
193
+ }
194
+ XCTAssertEqual ( rangeDescriptionString, " (45,99)(40,5)(28,12)(20,8)(18,2)(0,18) " )
195
+ XCTAssertEqual ( attrsDescriptionString, " [:][attribute.placeholder.key3:attribute.placeholder.value3][:][attribute.placeholder.key2:attribute.placeholder.value2][attribute.placeholder.key1:attribute.placeholder.value1,attribute.placeholder.key2:attribute.placeholder.value2][attribute.placeholder.key1:attribute.placeholder.value1] " )
196
+
197
+ let partialRange = NSRange ( location: 0 , length: 10 )
198
+
199
+ rangeDescriptionString = " "
200
+ attrsDescriptionString = " "
201
+ attrString. enumerateAttributes ( in: partialRange) { attrs, range, stop in
202
+ rangeDescriptionString. append ( self . describe ( range: range) )
203
+ attrsDescriptionString. append ( self . describe ( attrs: attrs) )
204
+ }
205
+ XCTAssertEqual ( rangeDescriptionString, " (0,10) " )
206
+ XCTAssertEqual ( attrsDescriptionString, " [attribute.placeholder.key1:attribute.placeholder.value1] " )
207
+
208
+ rangeDescriptionString = " "
209
+ attrsDescriptionString = " "
210
+ attrString. enumerateAttributes ( in: partialRange, options: [ . reverse] ) { attrs, range, stop in
211
+ rangeDescriptionString. append ( self . describe ( range: range) )
212
+ attrsDescriptionString. append ( self . describe ( attrs: attrs) )
213
+ }
214
+ XCTAssertEqual ( rangeDescriptionString, " (0,10) " )
215
+ XCTAssertEqual ( attrsDescriptionString, " [attribute.placeholder.key1:attribute.placeholder.value1] " )
216
+ }
217
+ }
218
+
219
+ fileprivate extension TestNSAttributedString {
220
+
221
+ fileprivate func describe( range: NSRange ) -> String {
222
+ return " ( \( range. location) , \( range. length) ) "
223
+ }
224
+
225
+ fileprivate func describe( attr: Any ? ) -> String {
226
+ if let attr = attr {
227
+ return " \( attr) " + " | "
228
+ } else {
229
+ return " nil " + " | "
230
+ }
231
+ }
232
+
233
+ fileprivate func describe( attrs: [ String : Any ] ) -> String {
234
+ if attrs. count > 0 {
235
+ return " [ " + attrs. map ( { " \( $0) : \( $1) " } ) . sorted ( by: { $0 < $1 } ) . joined ( separator: " , " ) + " ] "
236
+ } else {
237
+ return " [:] "
238
+ }
239
+ }
108
240
}
109
241
110
242
class TestNSMutableAttributedString : XCTestCase {
@@ -119,6 +251,5 @@ class TestNSMutableAttributedString : XCTestCase {
119
251
let string = " Lorem 😀 ipsum dolor sit amet, consectetur adipiscing elit. ⌘ Phasellus consectetur et sem vitae consectetur. Nam venenatis lectus a laoreet blandit. ಠ_ರೃ "
120
252
let mutableAttrString = NSMutableAttributedString ( string: string)
121
253
XCTAssertEqual ( mutableAttrString. mutableString, NSMutableString ( string: string) )
122
- }
123
-
254
+ }
124
255
}
0 commit comments