|
| 1 | +namespace FSharp.Collections.Immutable.Tests |
| 2 | + |
| 3 | +open System |
| 4 | +open System.Collections.Generic |
| 5 | +open FSharp.Collections.Immutable |
| 6 | +open Microsoft.VisualStudio.TestTools.UnitTesting |
| 7 | + |
| 8 | +/// <summary>An element whose equality and comparison look at its key only.</summary> |
| 9 | +/// <remarks> |
| 10 | +/// Elements with the same key and different tags compare equal, so a sort that compares whole elements, such as |
| 11 | +/// <c>sort</c>, shows on them whether it keeps the order of equal elements. |
| 12 | +/// </remarks> |
| 13 | +[<CustomEquality; CustomComparison>] |
| 14 | +type KeyedItem = { |
| 15 | + /// The key that the equality and the comparison look at. |
| 16 | + Key : int |
| 17 | + /// The tag that tells the elements with the same key apart. |
| 18 | + Tag : char |
| 19 | +} with |
| 20 | + |
| 21 | + override this.Equals other = |
| 22 | + match other with |
| 23 | + | :? KeyedItem as other -> this.Key = other.Key |
| 24 | + | _ -> false |
| 25 | + |
| 26 | + override this.GetHashCode () = this.Key.GetHashCode () |
| 27 | + |
| 28 | + // F# generic comparison calls the non-generic interface |
| 29 | + interface IComparable with |
| 30 | + member this.CompareTo other = |
| 31 | + match other with |
| 32 | + | :? KeyedItem as other -> compare this.Key other.Key |
| 33 | + | _ -> invalidArg (nameof other) "A keyed item compares with keyed items only" |
| 34 | + |
| 35 | +/// <summary>The sorting functions of a module that sorts stably, for the contract tests.</summary> |
| 36 | +/// <remarks> |
| 37 | +/// The elements are the pairs of <see cref="P:FSharp.Collections.Immutable.Tests.Inputs.pairs"/>: the functions sort them |
| 38 | +/// by the key in the first component, and the tag in the second component shows the order of the elements with equal keys. |
| 39 | +/// <c>Sort</c> and <c>SortDescending</c> compare whole elements and pairs never compare equal, so they take |
| 40 | +/// <see cref="T:FSharp.Collections.Immutable.Tests.KeyedItem"/> elements instead, and the instance creates and reads the |
| 41 | +/// collection itself, as <c>SeqOps.Unzip</c> does for its pairs. |
| 42 | +/// </remarks> |
| 43 | +type StableSortOps<'C> = { |
| 44 | + /// The name of the module, for the messages of the assertions. |
| 45 | + Name : string |
| 46 | + /// Creates a collection that enumerates the given elements in their order. |
| 47 | + OfSeq : seq<struct (int * char)> -> 'C |
| 48 | + /// The elements in the order in which the collection enumerates them. |
| 49 | + ToList : 'C -> struct (int * char) list |
| 50 | + |
| 51 | + /// Sorts a collection of the given keyed items. |
| 52 | + Sort : KeyedItem list -> KeyedItem list |
| 53 | + SortBy : (struct (int * char) -> int) -> 'C -> 'C |
| 54 | + SortByDescending : (struct (int * char) -> int) -> 'C -> 'C |
| 55 | + /// Sorts a collection of the given keyed items in descending order. |
| 56 | + SortDescending : KeyedItem list -> KeyedItem list |
| 57 | + SortWith : (struct (int * char) -> struct (int * char) -> int) -> 'C -> 'C |
| 58 | + SortWithComparer : IComparer<struct (int * char)> -> 'C -> 'C |
| 59 | +} |
| 60 | + |
| 61 | +/// <summary> |
| 62 | +/// The contract of the modules that sort stably, as <see cref="T:Microsoft.FSharp.Collections.ListModule"/> does: |
| 63 | +/// <c>IndexedList</c>, <c>IIndexedList</c>, <c>Queue</c>, <c>IQueue</c>, <c>Stack</c> and <c>IStack</c> (ADR 0001, D5). |
| 64 | +/// </summary> |
| 65 | +/// <remarks> |
| 66 | +/// The expectations come from the stable sorts of <c>List</c> over the elements in the order in which the collection |
| 67 | +/// enumerates them. <c>FlatList</c> sorts unstably, as <c>Array</c> does, and has no such contract. |
| 68 | +/// </remarks> |
| 69 | +[<AbstractClass>] |
| 70 | +type StableSortContract<'C> (ops : StableSortOps<'C>) = |
| 71 | + |
| 72 | + static let key (struct (key : int, _ : char)) = key |
| 73 | + |
| 74 | + static let compareKeys (left : struct (int * char)) (right : struct (int * char)) = compare (key left) (key right) |
| 75 | + |
| 76 | + /// The keyed items of the pairs of the input, in their order. |
| 77 | + static let keyedItems = |
| 78 | + Inputs.pairs |
| 79 | + |> List.map (fun struct (key, tag) -> { Key = key; Tag = tag }) |
| 80 | + |
| 81 | + /// The keys and the tags of keyed items, which tell apart the items that compare equal. |
| 82 | + static let keysAndTags (items : KeyedItem list) = items |> List.map (fun item -> struct (item.Key, item.Tag)) |
| 83 | + |
| 84 | + /// The collection of the input pairs and the elements that it enumerates. |
| 85 | + let pairs () = |
| 86 | + let collection = ops.OfSeq Inputs.pairs |
| 87 | + struct (collection, ops.ToList collection) |
| 88 | + |
| 89 | + [<TestMethod; TestCategory(nameof IndexedList.sort)>] |
| 90 | + member _.``sort keeps the order of the elements that compare equal`` () = |
| 91 | + Assert.AreEqual<struct (int * char) list>( |
| 92 | + List.sort keyedItems |> keysAndTags, |
| 93 | + ops.Sort keyedItems |> keysAndTags, |
| 94 | + $"%s{ops.Name}.sort must keep the order of equal elements, as List.sort does" |
| 95 | + ) |
| 96 | + |
| 97 | + [<TestMethod; TestCategory(nameof IndexedList.sortBy)>] |
| 98 | + member _.``sortBy keeps the order of the elements with equal keys`` () = |
| 99 | + let struct (collection, elements) = pairs () |
| 100 | + |
| 101 | + Assert.AreEqual<struct (int * char) list>( |
| 102 | + List.sortBy key elements, |
| 103 | + ops.ToList (ops.SortBy key collection), |
| 104 | + $"%s{ops.Name}.sortBy must keep the order of equal keys, as List.sortBy does" |
| 105 | + ) |
| 106 | + |
| 107 | + [<TestMethod; TestCategory(nameof IndexedList.sortByDescending)>] |
| 108 | + member _.``sortByDescending keeps the order of the elements with equal keys`` () = |
| 109 | + let struct (collection, elements) = pairs () |
| 110 | + |
| 111 | + Assert.AreEqual<struct (int * char) list>( |
| 112 | + List.sortByDescending key elements, |
| 113 | + ops.ToList (ops.SortByDescending key collection), |
| 114 | + $"%s{ops.Name}.sortByDescending must keep the order of equal keys, as List.sortByDescending does" |
| 115 | + ) |
| 116 | + |
| 117 | + [<TestMethod; TestCategory(nameof IndexedList.sortDescending)>] |
| 118 | + member _.``sortDescending keeps the order of the elements that compare equal`` () = |
| 119 | + Assert.AreEqual<struct (int * char) list>( |
| 120 | + List.sortDescending keyedItems |> keysAndTags, |
| 121 | + ops.SortDescending keyedItems |> keysAndTags, |
| 122 | + $"%s{ops.Name}.sortDescending must keep the order of equal elements, as List.sortDescending does" |
| 123 | + ) |
| 124 | + |
| 125 | + [<TestMethod; TestCategory(nameof IndexedList.sortWith)>] |
| 126 | + member _.``sortWith keeps the order of the elements that compare equal`` () = |
| 127 | + let struct (collection, elements) = pairs () |
| 128 | + |
| 129 | + for struct (direction, comparer) in |
| 130 | + [ |
| 131 | + struct ("ascending", compareKeys) |
| 132 | + struct ("descending", (fun left right -> compareKeys right left)) |
| 133 | + ] do |
| 134 | + Assert.AreEqual<struct (int * char) list>( |
| 135 | + List.sortWith comparer elements, |
| 136 | + ops.ToList (ops.SortWith comparer collection), |
| 137 | + $"%s{ops.Name}.sortWith %s{direction} must keep the order of equal elements, as List.sortWith does" |
| 138 | + ) |
| 139 | + |
| 140 | + [<TestMethod; TestCategory(nameof IndexedList.sortWithComparer)>] |
| 141 | + member _.``sortWithComparer keeps the order of the elements that compare equal`` () = |
| 142 | + let struct (collection, elements) = pairs () |
| 143 | + |
| 144 | + Assert.AreEqual<struct (int * char) list>( |
| 145 | + List.sortWith compareKeys elements, |
| 146 | + ops.ToList ( |
| 147 | + ops.SortWithComparer (Comparer<struct (int * char)>.Create (fun left right -> compareKeys left right)) collection |
| 148 | + ), |
| 149 | + $"%s{ops.Name}.sortWithComparer must keep the order of equal elements, as List.sortWith does" |
| 150 | + ) |
0 commit comments