@@ -206,22 +206,50 @@ end
206206end
207207
208208@testset " equality & hashing" begin
209- # singletons (identity and property lens) are object equal
210- for (l, r ) ∈ [
209+ # singletons (identity and property lens) are egal
210+ for (l1, l2 ) ∈ [
211211 @lens (_) => @lens (_),
212212 @lens (_. a) => @lens (_. a)
213213 ]
214- @test l === r
214+ @test l1 === l2
215+ @test l1 == l2
216+ @test hash (l1) == hash (l2)
215217 end
216218
217219 # composite and index lenses are structurally equal
218- for (l, r ) ∈ [
220+ for (l1, l2 ) ∈ [
219221 @lens (_[1 ]) => @lens (_[1 ])
220222 @lens (_. a[2 ]) => @lens (_. a[2 ])
223+ @lens (_. a. b[3 ]) => @lens (_. a. b[3 ])
221224 ]
222- @test l == r
223- @test hash (l ) == hash (r )
225+ @test l1 == l2
226+ @test hash (l1 ) == hash (l2 )
224227 end
228+
229+ # inequality
230+ for (l1, l2) ∈ [
231+ @lens (_[1 ]) => @lens (_[2 ])
232+ @lens (_. a[1 ]) => @lens (_. a[2 ])
233+ @lens (_. a[1 ]) => @lens (_. b[1 ])
234+ ]
235+ @test l1 != l2
236+ end
237+
238+ # Hash property: equality implies equal hashes, or in other terms:
239+ # lenses either have equal hashes or are unequal
240+ # Because collisions can occur theoretically (though unlikely), this is a property test,
241+ # not a unit test.
242+ random_lenses = (@lens (_. a[rand (Int)]) for _ in 1 : 1000 )
243+ @test all ((hash (l2) == hash (l1)) || (l1 != l2)
244+ for (l1, l2) in zip (random_lenses, random_lenses))
245+
246+ # Lenses should hash differently from the underlying tuples, to avoid confusion.
247+ # To account for potential collisions, we check that the property holds with high
248+ # probability.
249+ @test count (hash (@lens (_[i])) != hash ((i,)) for i = 1 : 1000 ) > 900
250+
251+ # Same for tuples of tuples (√(1000) ≈ 32).
252+ @test count (hash (@lens (_[i][j])) != hash (((i,), (j,))) for i = 1 : 32 , j = 1 : 32 ) > 900
225253end
226254
227255
0 commit comments