Repository: Quick/Nimble (swift)

Issue:
Version 11.x regression: tuple comparison with `==`
- [x] I have read [CONTRIBUTING](https://github.com/Quick/Nimble/blob/main/CONTRIBUTING.md) and have done my best to follow them.

### What did you do?

```swift
expect(tuple) == (a, b)
```

### What did you expect to happen?

This used to compile with Nimble 10.0.0

### What actually happened instead?

> Type '(a: A, b: B)' cannot conform to 'Equatable'

### Environment

List the software versions you're using:

- Quick: 6.0.0
- Nimble: 11.1.0
- Xcode Version: 14.1.0
- Swift Version: 5.7.1

- Carthage: 0.38.0

Pull request:
Fixes #1014

This is something that broke when we made `Expectation` a protocol. Apparently, there's something in swift's type system that just does not work with this? I'm not going to search through the swift bug list for it, though.

So, the solution is to implement `==` with tuples for both SyncExpectation and AsyncExpectation.

Patch:
diff --git a/Sources/Nimble/Matchers/Equal+Tuple.swift b/Sources/Nimble/Matchers/Equal+Tuple.swift
index d5747808c..863376f69 100644
--- a/Sources/Nimble/Matchers/Equal+Tuple.swift
+++ b/Sources/Nimble/Matchers/Equal+Tuple.swift
@@ -10,20 +10,33 @@ public func equal<T1: Equatable, T2: Equatable>(
     equal(expectedValue, by: ==)
 }
 
-public func ==<Exp: Expectation, T1: Equatable, T2: Equatable>(
-    lhs: Exp,
+public func ==<T1: Equatable, T2: Equatable>(
+    lhs: SyncExpectation<(T1, T2)>,
     rhs: (T1, T2)?
-) where Exp.Value == (T1, T2) {
+) {
     lhs.to(equal(rhs))
 }
 
-public func !=<Exp: Expectation, T1: Equatable, T2: Equatable>(
-    lhs: Exp,
+public func ==<T1: Equatable, T2: Equatable>(
+    lhs: AsyncExpectation<(T1, T2)>,
     rhs: (T1, T2)?
-) where Exp.Value == (T1, T2) {
+) {
+    lhs.to(equal(rhs))
+}
+
+public func !=<T1: Equatable, T2: Equatable>(
+    lhs: SyncExpectation<(T1, T2)>,
+    rhs: (T1, T2)?
+) {
     lhs.toNot(equal(rhs))
 }
 
+public func !=<T1: Equatable, T2: Equatable>(
+    lhs: AsyncExpectation<(T1, T2)>,
+    rhs: (T1, T2)?
+) {
+    lhs.toNot(equal(rhs))
+}
 
 // MARK: Tuple3
 
@@ -35,17 +48,32 @@ public func equal<T1: Equatable, T2: Equatable, T3: Equatable>(
     equal(expectedValue, by: ==)
 }
 
-public func ==<Exp: Expectation, T1: Equatable, T2: Equatable, T3: Equatable>(
-    lhs: Exp,
+public func ==<T1: Equatable, T2: Equatable, T3: Equatable>(
+    lhs: SyncExpectation<(T1, T2, T3)>,
     rhs: (T1, T2, T3)?
-) where Exp.Value == (T1, T2, T3) {
+) {
     lhs.to(equal(rhs))
 }
 
-public func !=<Exp: Expectation, T1: Equatable, T2: Equatable, T3: Equatable>(
-    lhs: Exp,
+public func ==<T1: Equatable, T2: Equatable, T3: Equatable>(
+    lhs: AsyncExpectation<(T1, T2, T3)>,
     rhs: (T1, T2, T3)?
-) where Exp.Value == (T1, T2, T3) {
+) {
+    lhs.to(equal(rhs))
+}
+
+
+public func !=<T1: Equatable, T2: Equatable, T3: Equatable>(
+    lhs: SyncExpectation<(T1, T2, T3)>,
+    rhs: (T1, T2, T3)?
+) {
+    lhs.toNot(equal(rhs))
+}
+
+public func !=<T1: Equatable, T2: Equatable, T3: Equatable>(
+    lhs: AsyncExpectation<(T1, T2, T3)>,
+    rhs: (T1, T2, T3)?
+) {
     lhs.toNot(equal(rhs))
 }
 
@@ -60,17 +88,31 @@ public func equal<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable>(
     equal(expectedValue, by: ==)
 }
 
-public func ==<Exp: Expectation, T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable>(
-    lhs: Exp,
+public func ==<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable>(
+    lhs: SyncExpectation<(T1, T2, T3, T4)>,
     rhs: (T1, T2, T3, T4)?
-) where Exp.Value == (T1, T2, T3, T4) {
+) {
     lhs.to(equal(rhs))
 }
 
-public func !=<Exp: Expectation, T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable>(
-    lhs: Exp,
+public func ==<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable>(
+    lhs: AsyncExpectation<(T1, T2, T3, T4)>,
     rhs: (T1, T2, T3, T4)?
-) where Exp.Value == (T1, T2, T3, T4) {
+) {
+    lhs.to(equal(rhs))
+}
+
+public func !=<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable>(
+    lhs: SyncExpectation<(T1, T2, T3, T4)>,
+    rhs: (T1, T2, T3, T4)?
+) {
+    lhs.toNot(equal(rhs))
+}
+
+public func !=<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable>(
+    lhs: AsyncExpectation<(T1, T2, T3, T4)>,
+    rhs: (T1, T2, T3, T4)?
+) {
     lhs.toNot(equal(rhs))
 }
 
@@ -85,17 +127,32 @@ public func equal<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5
     equal(expectedValue, by: ==)
 }
 
-public func ==<Exp: Expectation, T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable>(
-    lhs: Exp,
+public func ==<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable>(
+    lhs: SyncExpectation<(T1, T2, T3, T4, T5)>,
+    rhs: (T1, T2, T3, T4, T5)?
+) {
+    lhs.to(equal(rhs))
+}
+
+public func ==<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable>(
+    lhs: AsyncExpectation<(T1, T2, T3, T4, T5)>,
     rhs: (T1, T2, T3, T4, T5)?
-) where Exp.Value == (T1, T2, T3, T4, T5) {
+) {
     lhs.to(equal(rhs))
 }
 
-public func !=<Exp: Expectation, T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable>(
-    lhs: Exp,
+
+public func !=<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable>(
+    lhs: SyncExpectation<(T1, T2, T3, T4, T5)>,
+    rhs: (T1, T2, T3, T4, T5)?
+) {
+    lhs.toNot(equal(rhs))
+}
+
+public func !=<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable>(
+    lhs: AsyncExpectation<(T1, T2, T3, T4, T5)>,
     rhs: (T1, T2, T3, T4, T5)?
-) where Exp.Value == (T1, T2, T3, T4, T5) {
+) {
     lhs.toNot(equal(rhs))
 }
 
@@ -110,17 +167,31 @@ public func equal<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5
     equal(expectedValue, by: ==)
 }
 
-public func ==<Exp: Expectation, T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable, T6: Equatable>(
-    lhs: Exp,
+public func ==<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable, T6: Equatable>(
+    lhs: SyncExpectation<(T1, T2, T3, T4, T5, T6)>,
+    rhs: (T1, T2, T3, T4, T5, T6)?
+) {
+    lhs.to(equal(rhs))
+}
+
+public func ==<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable, T6: Equatable>(
+    lhs: AsyncExpectation<(T1, T2, T3, T4, T5, T6)>,
     rhs: (T1, T2, T3, T4, T5, T6)?
-) where Exp.Value == (T1, T2, T3, T4, T5, T6) {
+) {
     lhs.to(equal(rhs))
 }
 
-public func !=<Exp: Expectation, T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable, T6: Equatable>(
-    lhs: Exp,
+public func !=<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable, T6: Equatable>(
+    lhs: SyncExpectation<(T1, T2, T3, T4, T5, T6)>,
+    rhs: (T1, T2, T3, T4, T5, T6)?
+) {
+    lhs.toNot(equal(rhs))
+}
+
+public func !=<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable, T5: Equatable, T6: Equatable>(
+    lhs: AsyncExpectation<(T1, T2, T3, T4, T5, T6)>,
     rhs: (T1, T2, T3, T4, T5, T6)?
-) where Exp.Value == (T1, T2, T3, T4, T5, T6) {
+) {
     lhs.toNot(equal(rhs))
 }
 


Test patch:
diff --git a/Tests/NimbleTests/Matchers/EqualTest.swift b/Tests/NimbleTests/Matchers/EqualTest.swift
index 92113e289..309998a3d 100644
--- a/Tests/NimbleTests/Matchers/EqualTest.swift
+++ b/Tests/NimbleTests/Matchers/EqualTest.swift
@@ -283,6 +283,14 @@ final class EqualTest: XCTestCase { // swiftlint:disable:this type_body_length
         expect((1, "2", 3, four: "4")).to(equal((1, "2", 3, "4")))
         expect((1, "2", 3, four: "4", 5)).to(equal((1, "2", 3, "4", five: 5)))
         expect((1, "2", 3, four: "4", 5, "6")).to(equal((1, "2", 3, "4", five: 5, "6")))
+
+        expect((1, "2")) == (1, "2")
+        expect((1, two: "2")) == (1, two: "2")
+        expect((1, "2", 3)) == (1, "2", 3)
+        expect((1, "2", three: 3)) == (1, "2", three: 3)
+        expect((1, "2", 3, four: "4")) == (1, "2", 3, "4")
+        expect((1, "2", 3, four: "4", 5)) == (1, "2", 3, "4", five: 5)
+        expect((1, "2", 3, four: "4", 5, "6")) == (1, "2", 3, "4", five: 5, "6")
     }
 
     // see: https://github.com/Quick/Nimble/issues/867 and https://github.com/Quick/Nimble/issues/937