github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/tpl/collections/sort_test.go (about) 1 // Copyright 2017 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package collections 15 16 import ( 17 "context" 18 "fmt" 19 "reflect" 20 "testing" 21 22 "github.com/gohugoio/hugo/common/maps" 23 ) 24 25 type stringsSlice []string 26 27 func TestSort(t *testing.T) { 28 t.Parallel() 29 30 ns := newNs() 31 32 type ts struct { 33 MyInt int 34 MyFloat float64 35 MyString string 36 } 37 type mid struct { 38 Tst TstX 39 } 40 41 for i, test := range []struct { 42 seq any 43 sortByField any 44 sortAsc string 45 expect any 46 }{ 47 {[]string{"class1", "class2", "class3"}, nil, "asc", []string{"class1", "class2", "class3"}}, 48 {[]string{"class3", "class1", "class2"}, nil, "asc", []string{"class1", "class2", "class3"}}, 49 {[]string{"CLASS3", "class1", "class2"}, nil, "asc", []string{"class1", "class2", "CLASS3"}}, 50 // Issue 6023 51 {stringsSlice{"class3", "class1", "class2"}, nil, "asc", stringsSlice{"class1", "class2", "class3"}}, 52 53 {[]int{1, 2, 3, 4, 5}, nil, "asc", []int{1, 2, 3, 4, 5}}, 54 {[]int{5, 4, 3, 1, 2}, nil, "asc", []int{1, 2, 3, 4, 5}}, 55 // test sort key parameter is forcibly set empty 56 {[]string{"class3", "class1", "class2"}, map[int]string{1: "a"}, "asc", []string{"class1", "class2", "class3"}}, 57 // test map sorting by keys 58 {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, nil, "asc", []int{10, 20, 30, 40, 50}}, 59 {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, nil, "asc", []int{30, 20, 10, 40, 50}}, 60 {map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, nil, "asc", []string{"10", "20", "30", "40", "50"}}, 61 {map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, nil, "asc", []string{"30", "20", "10", "40", "50"}}, 62 {map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, nil, "asc", []string{"50", "40", "10", "30", "20"}}, 63 {map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, nil, "asc", []string{"10", "20", "30", "40", "50"}}, 64 {map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, nil, "asc", []string{"30", "20", "10", "40", "50"}}, 65 {map[float64]string{3.3: "10", 2.3: "20", 1.3: "30", 4.3: "40", 5.3: "50"}, nil, "asc", []string{"30", "20", "10", "40", "50"}}, 66 // test map sorting by value 67 {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "value", "asc", []int{10, 20, 30, 40, 50}}, 68 {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "value", "asc", []int{10, 20, 30, 40, 50}}, 69 // test map sorting by field value 70 { 71 map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}}, 72 "MyInt", 73 "asc", 74 []ts{{10, 10.5, "ten"}, {20, 20.5, "twenty"}, {30, 30.5, "thirty"}, {40, 40.5, "forty"}, {50, 50.5, "fifty"}}, 75 }, 76 { 77 map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}}, 78 "MyFloat", 79 "asc", 80 []ts{{10, 10.5, "ten"}, {20, 20.5, "twenty"}, {30, 30.5, "thirty"}, {40, 40.5, "forty"}, {50, 50.5, "fifty"}}, 81 }, 82 { 83 map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}}, 84 "MyString", 85 "asc", 86 []ts{{50, 50.5, "fifty"}, {40, 40.5, "forty"}, {10, 10.5, "ten"}, {30, 30.5, "thirty"}, {20, 20.5, "twenty"}}, 87 }, 88 // test sort desc 89 {[]string{"class1", "class2", "class3"}, "value", "desc", []string{"class3", "class2", "class1"}}, 90 {[]string{"class3", "class1", "class2"}, "value", "desc", []string{"class3", "class2", "class1"}}, 91 // test sort by struct's method 92 { 93 []TstX{{A: "i", B: "j"}, {A: "e", B: "f"}, {A: "c", B: "d"}, {A: "g", B: "h"}, {A: "a", B: "b"}}, 94 "TstRv", 95 "asc", 96 []TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}}, 97 }, 98 { 99 []*TstX{{A: "i", B: "j"}, {A: "e", B: "f"}, {A: "c", B: "d"}, {A: "g", B: "h"}, {A: "a", B: "b"}}, 100 "TstRp", 101 "asc", 102 []*TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}}, 103 }, 104 // Lower case Params, slice 105 { 106 []TstParams{{params: maps.Params{"color": "indigo"}}, {params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}}, 107 ".Params.COLOR", 108 "asc", 109 []TstParams{{params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}, {params: maps.Params{"color": "indigo"}}}, 110 }, 111 // Lower case Params, map 112 { 113 map[string]TstParams{"1": {params: maps.Params{"color": "indigo"}}, "2": {params: maps.Params{"color": "blue"}}, "3": {params: maps.Params{"color": "green"}}}, 114 ".Params.CoLoR", 115 "asc", 116 []TstParams{{params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}, {params: maps.Params{"color": "indigo"}}}, 117 }, 118 // test map sorting by struct's method 119 { 120 map[string]TstX{"1": {A: "i", B: "j"}, "2": {A: "e", B: "f"}, "3": {A: "c", B: "d"}, "4": {A: "g", B: "h"}, "5": {A: "a", B: "b"}}, 121 "TstRv", 122 "asc", 123 []TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}}, 124 }, 125 { 126 map[string]*TstX{"1": {A: "i", B: "j"}, "2": {A: "e", B: "f"}, "3": {A: "c", B: "d"}, "4": {A: "g", B: "h"}, "5": {A: "a", B: "b"}}, 127 "TstRp", 128 "asc", 129 []*TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}}, 130 }, 131 // test sort by dot chaining key argument 132 { 133 []map[string]TstX{{"foo": TstX{A: "e", B: "f"}}, {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}}, 134 "foo.A", 135 "asc", 136 []map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}}, 137 }, 138 { 139 []map[string]TstX{{"foo": TstX{A: "e", B: "f"}}, {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}}, 140 ".foo.A", 141 "asc", 142 []map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}}, 143 }, 144 { 145 []map[string]TstX{{"foo": TstX{A: "e", B: "f"}}, {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}}, 146 "foo.TstRv", 147 "asc", 148 []map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}}, 149 }, 150 { 151 []map[string]*TstX{{"foo": &TstX{A: "e", B: "f"}}, {"foo": &TstX{A: "a", B: "b"}}, {"foo": &TstX{A: "c", B: "d"}}}, 152 "foo.TstRp", 153 "asc", 154 []map[string]*TstX{{"foo": &TstX{A: "a", B: "b"}}, {"foo": &TstX{A: "c", B: "d"}}, {"foo": &TstX{A: "e", B: "f"}}}, 155 }, 156 { 157 []map[string]mid{{"foo": mid{Tst: TstX{A: "e", B: "f"}}}, {"foo": mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": mid{Tst: TstX{A: "c", B: "d"}}}}, 158 "foo.Tst.A", 159 "asc", 160 []map[string]mid{{"foo": mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": mid{Tst: TstX{A: "e", B: "f"}}}}, 161 }, 162 { 163 []map[string]mid{{"foo": mid{Tst: TstX{A: "e", B: "f"}}}, {"foo": mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": mid{Tst: TstX{A: "c", B: "d"}}}}, 164 "foo.Tst.TstRv", 165 "asc", 166 []map[string]mid{{"foo": mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": mid{Tst: TstX{A: "e", B: "f"}}}}, 167 }, 168 // test map sorting by dot chaining key argument 169 { 170 map[string]map[string]TstX{"1": {"foo": TstX{A: "e", B: "f"}}, "2": {"foo": TstX{A: "a", B: "b"}}, "3": {"foo": TstX{A: "c", B: "d"}}}, 171 "foo.A", 172 "asc", 173 []map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}}, 174 }, 175 { 176 map[string]map[string]TstX{"1": {"foo": TstX{A: "e", B: "f"}}, "2": {"foo": TstX{A: "a", B: "b"}}, "3": {"foo": TstX{A: "c", B: "d"}}}, 177 ".foo.A", 178 "asc", 179 []map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}}, 180 }, 181 { 182 map[string]map[string]TstX{"1": {"foo": TstX{A: "e", B: "f"}}, "2": {"foo": TstX{A: "a", B: "b"}}, "3": {"foo": TstX{A: "c", B: "d"}}}, 183 "foo.TstRv", 184 "asc", 185 []map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}}, 186 }, 187 { 188 map[string]map[string]*TstX{"1": {"foo": &TstX{A: "e", B: "f"}}, "2": {"foo": &TstX{A: "a", B: "b"}}, "3": {"foo": &TstX{A: "c", B: "d"}}}, 189 "foo.TstRp", 190 "asc", 191 []map[string]*TstX{{"foo": &TstX{A: "a", B: "b"}}, {"foo": &TstX{A: "c", B: "d"}}, {"foo": &TstX{A: "e", B: "f"}}}, 192 }, 193 { 194 map[string]map[string]mid{"1": {"foo": mid{Tst: TstX{A: "e", B: "f"}}}, "2": {"foo": mid{Tst: TstX{A: "a", B: "b"}}}, "3": {"foo": mid{Tst: TstX{A: "c", B: "d"}}}}, 195 "foo.Tst.A", 196 "asc", 197 []map[string]mid{{"foo": mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": mid{Tst: TstX{A: "e", B: "f"}}}}, 198 }, 199 { 200 map[string]map[string]mid{"1": {"foo": mid{Tst: TstX{A: "e", B: "f"}}}, "2": {"foo": mid{Tst: TstX{A: "a", B: "b"}}}, "3": {"foo": mid{Tst: TstX{A: "c", B: "d"}}}}, 201 "foo.Tst.TstRv", 202 "asc", 203 []map[string]mid{{"foo": mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": mid{Tst: TstX{A: "e", B: "f"}}}}, 204 }, 205 // interface slice with missing elements 206 { 207 []any{ 208 map[any]any{"Title": "Foo", "Weight": 10}, 209 map[any]any{"Title": "Bar"}, 210 map[any]any{"Title": "Zap", "Weight": 5}, 211 }, 212 "Weight", 213 "asc", 214 []any{ 215 map[any]any{"Title": "Bar"}, 216 map[any]any{"Title": "Zap", "Weight": 5}, 217 map[any]any{"Title": "Foo", "Weight": 10}, 218 }, 219 }, 220 // test boolean values 221 {[]bool{false, true, false}, "value", "asc", []bool{false, false, true}}, 222 {[]bool{false, true, false}, "value", "desc", []bool{true, false, false}}, 223 // test error cases 224 {(*[]TstX)(nil), nil, "asc", false}, 225 {TstX{A: "a", B: "b"}, nil, "asc", false}, 226 { 227 []map[string]TstX{{"foo": TstX{A: "e", B: "f"}}, {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}}, 228 "foo.NotAvailable", 229 "asc", 230 false, 231 }, 232 { 233 map[string]map[string]TstX{"1": {"foo": TstX{A: "e", B: "f"}}, "2": {"foo": TstX{A: "a", B: "b"}}, "3": {"foo": TstX{A: "c", B: "d"}}}, 234 "foo.NotAvailable", 235 "asc", 236 false, 237 }, 238 {nil, nil, "asc", false}, 239 } { 240 t.Run(fmt.Sprintf("test%d", i), func(t *testing.T) { 241 var result any 242 var err error 243 if test.sortByField == nil { 244 result, err = ns.Sort(context.Background(), test.seq) 245 } else { 246 result, err = ns.Sort(context.Background(), test.seq, test.sortByField, test.sortAsc) 247 } 248 249 if b, ok := test.expect.(bool); ok && !b { 250 if err == nil { 251 t.Fatal("Sort didn't return an expected error") 252 } 253 } else { 254 if err != nil { 255 t.Fatalf("failed: %s", err) 256 } 257 if !reflect.DeepEqual(result, test.expect) { 258 t.Fatalf("Sort called on sequence: %#v | sortByField: `%v` | got\n%#v but expected\n%#v", test.seq, test.sortByField, result, test.expect) 259 } 260 } 261 }) 262 } 263 }