@@ -117,7 +117,52 @@ func XCTAssertUnorderedEqualSequences<S1: Sequence, S2: Sequence>(
117
117
if !s1. isEmpty {
118
118
fail ( " first sequence contains \( s1) missing from second sequence " )
119
119
}
120
+ }
120
121
122
+ /// Asserts two sequences contain exactly the same elements but not necessarily
123
+ /// in the same order.
124
+ /// - Complexity: O(*n* + *m*) where *n* is the number of elements in the first
125
+ /// sequence and *m* is the number of elements in the second sequence
126
+ func XCTAssertUnorderedEqualSequences< S1: Sequence , S2: Sequence > (
127
+ _ expression1: @autoclosure ( ) throws -> S1 ,
128
+ _ expression2: @autoclosure ( ) throws -> S2 ,
129
+ _ message: @autoclosure ( ) -> String = " " ,
130
+ file: StaticString = #file, line: UInt = #line
131
+ ) rethrows where S1. Element: Hashable , S1. Element == S2 . Element {
132
+ func fail( _ reason: String ) {
133
+ let message = message ( )
134
+ XCTFail ( message. isEmpty ? reason : " \( message) - \( reason) " ,
135
+ file: file, line: line)
136
+ }
137
+
138
+ // Map the elements of the first sequence to the number of times it appeared
139
+ // in the sequence
140
+ var s1 = [ S1 . Element: Int] ( try expression1 ( ) . lazy. map ( {
141
+ ( $0, 1 )
142
+ } ) , uniquingKeysWith: {
143
+ $0 + $1
144
+ } )
145
+
146
+ var missing : [ S1 . Element ] = [ ]
147
+ for elt in try expression2 ( ) {
148
+ if let count = s1 [ elt] {
149
+ if count > 1 {
150
+ s1 [ elt] = ( count - 1 )
151
+ } else {
152
+ s1 [ elt] = nil
153
+ }
154
+ } else {
155
+ missing. append ( elt)
156
+ }
157
+ }
158
+
159
+ if !missing. isEmpty {
160
+ fail ( " first sequence missing ' \( missing) ' elements from second sequence " )
161
+ }
162
+
163
+ if !s1. isEmpty {
164
+ fail ( " second sequence missing one or more occurrences of ' \( s1. keys) ' elements from first sequence " )
165
+ }
121
166
}
122
167
123
168
func XCTAssertEqualSequences< S1: Sequence , S2: Sequence > (
0 commit comments