@@ -117,7 +117,52 @@ func XCTAssertUnorderedEqualSequences<S1: Sequence, S2: Sequence>(
117117 if !s1. isEmpty {
118118 fail ( " first sequence contains \( s1) missing from second sequence " )
119119 }
120+ }
120121
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+ }
121166}
122167
123168func XCTAssertEqualSequences< S1: Sequence , S2: Sequence > (
0 commit comments