Skip to content

Commit c73ad52

Browse files
xperiandriclaude
andcommitted
fixup! test: FrozenSet against the set contracts
Tests of the gaps of the sets report (package B4): - `FrozenSetOps` fills the new `OfReadOnlySpan` and `OfSpan` fields of `SetCreationOps` and `SetCustomComparerOps`, so the shared creation contract runs `ofReadOnlySpan` and `ofSpan` of the module and of the struct on a copy of each input that it changes after the call. - `FrozenSetTests` lists the new functions in the tables of the creation functions and of the members of the struct, which check the comparer and the element that stays of "apple" and "APPLE". The shared contract states only that one element of each class stays, so two tests pin which one: the span members of the struct keep the first element in both orders, and the span functions of the module keep the first of two equal string instances, in both orders. - A third test pins the rule that the remarks of `CustomComparerFrozenSet.ofSeq` state: it returns a frozen set whose comparer equals its comparer, not only the same instance (ADR 0001, D14). Each new behavior was broken once in the code, 10 mutations (the comparer and the element that stays of the four span creators, a dropped element, and the instance test of `ofSeq`), and a test failed each time. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent 86dec96 commit c73ad52

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

‎tests/FSharp.Collections.Immutable.Tests/FrozenSet/FrozenSetOps.fs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
[<RequireQualifiedAccess>]
33
module FSharp.Collections.Immutable.Tests.FrozenSetOps
44

5+
open System
56
open System.Collections.Frozen
67
open System.Collections.Generic
78
open FSharp.Collections.Immutable
@@ -75,7 +76,9 @@ let creation : SetCreationOps<FrozenSet<int>> =
7576
Empty = fun () -> FrozenSet.empty
7677
OfArray = FrozenSet.ofArray
7778
OfList = FrozenSet.ofList
79+
OfReadOnlySpan = fun array -> FrozenSet.ofReadOnlySpan (ReadOnlySpan<int> array)
7880
OfSeq = FrozenSet.ofSeq
81+
OfSpan = fun array -> FrozenSet.ofSpan (Span<int> array)
7982
Singleton = FrozenSet.singleton
8083

8184
CustomComparer = {
@@ -84,7 +87,9 @@ let creation : SetCreationOps<FrozenSet<int>> =
8487
Empty = fun () -> custom.empty
8588
OfArray = fun array -> custom.ofArray array
8689
OfList = fun elements -> custom.ofList elements
90+
OfReadOnlySpan = fun array -> custom.ofReadOnlySpan (ReadOnlySpan<int> array)
8791
OfSeq = fun elements -> custom.ofSeq elements
92+
OfSpan = fun array -> custom.ofSpan (Span<int> array)
8893
Singleton = fun value -> custom.singleton value
8994
}
9095
}

‎tests/FSharp.Collections.Immutable.Tests/FrozenSet/FrozenSetTests.fs‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace FSharp.Collections.Immutable.Tests
33
open System
44
open System.Collections.Frozen
55
open System.Collections.Generic
6+
open System.Globalization
67
open System.Runtime.InteropServices
78
open FSharp.Collections.Immutable
89
open Microsoft.VisualStudio.TestTools.UnitTesting
@@ -154,7 +155,9 @@ type FrozenSetTests () =
154155
nameof FrozenSet.ofArray, (fun () -> FrozenSet.ofArray [| "a" |])
155156
// The function under test takes a list
156157
nameof FrozenSet.ofList, (fun () -> FrozenSet.ofList [ "a" ])
158+
nameof FrozenSet.ofReadOnlySpan, (fun () -> FrozenSet.ofReadOnlySpan (ReadOnlySpan<string> [| "a" |]))
157159
nameof FrozenSet.ofSeq, (fun () -> FrozenSet.ofSeq (Seq.singleton "a"))
160+
nameof FrozenSet.ofSpan, (fun () -> FrozenSet.ofSpan (Span<string> [| "a" |]))
158161
nameof FrozenSet.singleton, (fun () -> FrozenSet.singleton "a")
159162
|]
160163

@@ -165,7 +168,9 @@ type FrozenSetTests () =
165168
nameof caseless.ofArray, (fun () -> caseless.ofArray [| "apple"; "APPLE" |])
166169
// The member under test takes a list
167170
nameof caseless.ofList, (fun () -> caseless.ofList [ "apple"; "APPLE" ])
171+
nameof caseless.ofReadOnlySpan, (fun () -> caseless.ofReadOnlySpan (ReadOnlySpan<string> [| "apple"; "APPLE" |]))
168172
nameof caseless.ofSeq, (fun () -> caseless.ofSeq (Seq.ofArray [| "apple"; "APPLE" |]))
173+
nameof caseless.ofSpan, (fun () -> caseless.ofSpan (Span<string> [| "apple"; "APPLE" |]))
169174
nameof caseless.singleton, (fun () -> caseless.singleton "apple" |> add "APPLE")
170175
|]
171176

@@ -382,6 +387,54 @@ type FrozenSetTests () =
382387
$"CustomComparerFrozenSet.%s{name} must keep the first of the elements that the comparer considers equal"
383388
)
384389

390+
// The shared contract states that one element of each class stays. Which one stays is a property of the BCL member that
391+
// creates the set, so each module pins it, here in both orders of the input.
392+
[<TestMethod;
393+
DataRow("ofReadOnlySpan", "apple", "APPLE");
394+
DataRow("ofReadOnlySpan", "APPLE", "apple");
395+
DataRow("ofSpan", "apple", "APPLE");
396+
DataRow("ofSpan", "APPLE", "apple")>]
397+
member _.``the span members of the struct keep the first of the elements that its comparer considers equal``
398+
(name : string, first : string, second : string)
399+
=
400+
let input = [| first; second |]
401+
402+
let set =
403+
match name with
404+
| "ofReadOnlySpan" -> caseless.ofReadOnlySpan (ReadOnlySpan<string> input)
405+
| "ofSpan" -> caseless.ofSpan (Span<string> input)
406+
| name -> failwith $"CustomComparerFrozenSet has no span member %s{name}"
407+
408+
Assert.AreEqual<string>(
409+
first,
410+
Assert.ContainsSingle (set, $"CustomComparerFrozenSet.%s{name} must compare without case"),
411+
$"CustomComparerFrozenSet.%s{name} must keep the first of the elements that the comparer considers equal"
412+
)
413+
414+
[<TestMethod; DataRow "ofReadOnlySpan"; DataRow "ofSpan">]
415+
member _.``the span functions keep the first of the elements that are equal in the default comparer`` (name : string) =
416+
// Two strings that are equal and are two instances, so the instance tells which one stays
417+
let first = String ('x', 3)
418+
let second = String ('x', 3)
419+
420+
let create (input : string array) =
421+
match name with
422+
| "ofReadOnlySpan" -> FrozenSet.ofReadOnlySpan (ReadOnlySpan<string> input)
423+
| "ofSpan" -> FrozenSet.ofSpan (Span<string> input)
424+
| name -> failwith $"FrozenSet has no span function %s{name}"
425+
426+
Assert.AreSame (
427+
first,
428+
Assert.ContainsSingle (create [| first; second |], $"FrozenSet.%s{name} must hold equal elements once"),
429+
$"FrozenSet.%s{name} must keep the first of the elements that are equal"
430+
)
431+
432+
Assert.AreSame (
433+
second,
434+
Assert.ContainsSingle (create [| second; first |], $"FrozenSet.%s{name} must hold equal elements once"),
435+
$"FrozenSet.%s{name} must keep the first of the elements that are equal in either order"
436+
)
437+
385438
[<TestMethod>]
386439
member _.``the struct gives an existing frozen set its comparer`` () =
387440
let set = FrozenSet.ofArray [| "apple"; "APPLE"; "Banana" |]
@@ -564,6 +617,22 @@ type FrozenSetTests () =
564617
"CustomComparerFrozenSet.ofSeq of a frozen set with the comparer must not copy it"
565618
)
566619

620+
// ToFrozenSet returns a frozen set whose comparer Equals the comparer, not only the same instance (ADR 0001, D14)
621+
[<TestMethod>]
622+
member _.``the struct returns a frozen set whose comparer equals its comparer`` () =
623+
let first : IEqualityComparer<string> = StringComparer.Create (CultureInfo.InvariantCulture, true)
624+
let second : IEqualityComparer<string> = StringComparer.Create (CultureInfo.InvariantCulture, true)
625+
Assert.AreNotSame (first, second, "The test needs two comparer instances")
626+
Assert.AreEqual<obj>(first, second, "The test needs two comparers that are equal")
627+
628+
let set = (FrozenSet.forComparer first).ofArray [| "apple"; "Banana" |]
629+
630+
Assert.AreSame (
631+
set,
632+
(FrozenSet.forComparer second).ofSeq set,
633+
"CustomComparerFrozenSet.ofSeq must return a frozen set whose comparer equals its comparer"
634+
)
635+
567636
[<TestMethod>]
568637
member _.``intersectMany of one frozen set returns it`` () =
569638
let set = words ()

0 commit comments

Comments
 (0)