pages_sort_test.go (7833B)
1 // Copyright 2019 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 page 15 16 import ( 17 "fmt" 18 "testing" 19 "time" 20 21 "github.com/gohugoio/hugo/resources/resource" 22 "github.com/google/go-cmp/cmp" 23 24 qt "github.com/frankban/quicktest" 25 ) 26 27 var eq = qt.CmpEquals( 28 cmp.Comparer(func(p1, p2 testPage) bool { 29 return p1.path == p2.path && p1.weight == p2.weight 30 }), 31 ) 32 33 func TestDefaultSort(t *testing.T) { 34 t.Parallel() 35 c := qt.New(t) 36 d1 := time.Now() 37 d2 := d1.Add(-1 * time.Hour) 38 d3 := d1.Add(-2 * time.Hour) 39 d4 := d1.Add(-3 * time.Hour) 40 41 p := createSortTestPages(4) 42 43 // first by weight 44 setSortVals([4]time.Time{d1, d2, d3, d4}, [4]string{"b", "a", "c", "d"}, [4]int{4, 3, 2, 1}, p) 45 SortByDefault(p) 46 47 c.Assert(p[0].Weight(), qt.Equals, 1) 48 49 // Consider zero weight, issue #2673 50 setSortVals([4]time.Time{d1, d2, d3, d4}, [4]string{"b", "a", "d", "c"}, [4]int{0, 0, 0, 1}, p) 51 SortByDefault(p) 52 53 c.Assert(p[0].Weight(), qt.Equals, 1) 54 55 // next by date 56 setSortVals([4]time.Time{d3, d4, d1, d2}, [4]string{"a", "b", "c", "d"}, [4]int{1, 1, 1, 1}, p) 57 SortByDefault(p) 58 c.Assert(p[0].Date(), qt.Equals, d1) 59 60 // finally by link title 61 setSortVals([4]time.Time{d3, d3, d3, d3}, [4]string{"b", "c", "a", "d"}, [4]int{1, 1, 1, 1}, p) 62 SortByDefault(p) 63 c.Assert(p[0].LinkTitle(), qt.Equals, "al") 64 c.Assert(p[1].LinkTitle(), qt.Equals, "bl") 65 c.Assert(p[2].LinkTitle(), qt.Equals, "cl") 66 } 67 68 // https://github.com/gohugoio/hugo/issues/4953 69 func TestSortByLinkTitle(t *testing.T) { 70 t.Parallel() 71 c := qt.New(t) 72 pages := createSortTestPages(6) 73 74 for i, p := range pages { 75 pp := p.(*testPage) 76 if i < 5 { 77 pp.title = fmt.Sprintf("title%d", i) 78 } 79 80 if i > 2 { 81 pp.linkTitle = fmt.Sprintf("linkTitle%d", i) 82 } 83 84 } 85 86 pages.shuffle() 87 88 bylt := pages.ByLinkTitle() 89 90 for i, p := range bylt { 91 if i < 3 { 92 c.Assert(p.LinkTitle(), qt.Equals, fmt.Sprintf("linkTitle%d", i+3)) 93 } else { 94 c.Assert(p.LinkTitle(), qt.Equals, fmt.Sprintf("title%d", i-3)) 95 } 96 } 97 } 98 99 func TestSortByN(t *testing.T) { 100 t.Parallel() 101 d1 := time.Now() 102 d2 := d1.Add(-2 * time.Hour) 103 d3 := d1.Add(-10 * time.Hour) 104 d4 := d1.Add(-20 * time.Hour) 105 106 p := createSortTestPages(4) 107 108 for i, this := range []struct { 109 sortFunc func(p Pages) Pages 110 assertFunc func(p Pages) bool 111 }{ 112 {(Pages).ByWeight, func(p Pages) bool { return p[0].Weight() == 1 }}, 113 {(Pages).ByTitle, func(p Pages) bool { return p[0].Title() == "ab" }}, 114 {(Pages).ByLinkTitle, func(p Pages) bool { return p[0].LinkTitle() == "abl" }}, 115 {(Pages).ByDate, func(p Pages) bool { return p[0].Date() == d4 }}, 116 {(Pages).ByPublishDate, func(p Pages) bool { return p[0].PublishDate() == d4 }}, 117 {(Pages).ByExpiryDate, func(p Pages) bool { return p[0].ExpiryDate() == d4 }}, 118 {(Pages).ByLastmod, func(p Pages) bool { return p[1].Lastmod() == d3 }}, 119 {(Pages).ByLength, func(p Pages) bool { return p[0].(resource.LengthProvider).Len() == len(p[0].(*testPage).content) }}, 120 } { 121 setSortVals([4]time.Time{d1, d2, d3, d4}, [4]string{"b", "ab", "cde", "fg"}, [4]int{0, 3, 2, 1}, p) 122 123 sorted := this.sortFunc(p) 124 if !this.assertFunc(sorted) { 125 t.Errorf("[%d] sort error", i) 126 } 127 } 128 } 129 130 func TestLimit(t *testing.T) { 131 t.Parallel() 132 c := qt.New(t) 133 p := createSortTestPages(10) 134 firstFive := p.Limit(5) 135 c.Assert(len(firstFive), qt.Equals, 5) 136 for i := 0; i < 5; i++ { 137 c.Assert(firstFive[i], qt.Equals, p[i]) 138 } 139 c.Assert(p.Limit(10), eq, p) 140 c.Assert(p.Limit(11), eq, p) 141 } 142 143 func TestPageSortReverse(t *testing.T) { 144 t.Parallel() 145 c := qt.New(t) 146 p1 := createSortTestPages(10) 147 c.Assert(p1[0].(*testPage).fuzzyWordCount, qt.Equals, 0) 148 c.Assert(p1[9].(*testPage).fuzzyWordCount, qt.Equals, 9) 149 p2 := p1.Reverse() 150 c.Assert(p2[0].(*testPage).fuzzyWordCount, qt.Equals, 9) 151 c.Assert(p2[9].(*testPage).fuzzyWordCount, qt.Equals, 0) 152 // cached 153 c.Assert(pagesEqual(p2, p1.Reverse()), qt.Equals, true) 154 } 155 156 func TestPageSortByParam(t *testing.T) { 157 t.Parallel() 158 c := qt.New(t) 159 var k any = "arbitrarily.nested" 160 161 unsorted := createSortTestPages(10) 162 delete(unsorted[9].Params(), "arbitrarily") 163 164 firstSetValue, _ := unsorted[0].Param(k) 165 secondSetValue, _ := unsorted[1].Param(k) 166 lastSetValue, _ := unsorted[8].Param(k) 167 unsetValue, _ := unsorted[9].Param(k) 168 169 c.Assert(firstSetValue, qt.Equals, "xyz100") 170 c.Assert(secondSetValue, qt.Equals, "xyz99") 171 c.Assert(lastSetValue, qt.Equals, "xyz92") 172 c.Assert(unsetValue, qt.Equals, nil) 173 174 sorted := unsorted.ByParam("arbitrarily.nested") 175 firstSetSortedValue, _ := sorted[0].Param(k) 176 secondSetSortedValue, _ := sorted[1].Param(k) 177 lastSetSortedValue, _ := sorted[8].Param(k) 178 unsetSortedValue, _ := sorted[9].Param(k) 179 180 c.Assert(firstSetSortedValue, qt.Equals, firstSetValue) 181 c.Assert(lastSetSortedValue, qt.Equals, secondSetValue) 182 c.Assert(secondSetSortedValue, qt.Equals, lastSetValue) 183 c.Assert(unsetSortedValue, qt.Equals, unsetValue) 184 } 185 186 func TestPageSortByParamNumeric(t *testing.T) { 187 t.Parallel() 188 c := qt.New(t) 189 190 var k any = "arbitrarily.nested" 191 192 n := 10 193 unsorted := createSortTestPages(n) 194 for i := 0; i < n; i++ { 195 v := 100 - i 196 if i%2 == 0 { 197 v = 100.0 - i 198 } 199 200 unsorted[i].(*testPage).params = map[string]any{ 201 "arbitrarily": map[string]any{ 202 "nested": v, 203 }, 204 } 205 } 206 delete(unsorted[9].Params(), "arbitrarily") 207 208 firstSetValue, _ := unsorted[0].Param(k) 209 secondSetValue, _ := unsorted[1].Param(k) 210 lastSetValue, _ := unsorted[8].Param(k) 211 unsetValue, _ := unsorted[9].Param(k) 212 213 c.Assert(firstSetValue, qt.Equals, 100) 214 c.Assert(secondSetValue, qt.Equals, 99) 215 c.Assert(lastSetValue, qt.Equals, 92) 216 c.Assert(unsetValue, qt.Equals, nil) 217 218 sorted := unsorted.ByParam("arbitrarily.nested") 219 firstSetSortedValue, _ := sorted[0].Param(k) 220 secondSetSortedValue, _ := sorted[1].Param(k) 221 lastSetSortedValue, _ := sorted[8].Param(k) 222 unsetSortedValue, _ := sorted[9].Param(k) 223 224 c.Assert(firstSetSortedValue, qt.Equals, 92) 225 c.Assert(secondSetSortedValue, qt.Equals, 93) 226 c.Assert(lastSetSortedValue, qt.Equals, 100) 227 c.Assert(unsetSortedValue, qt.Equals, unsetValue) 228 } 229 230 func BenchmarkSortByWeightAndReverse(b *testing.B) { 231 p := createSortTestPages(300) 232 233 b.ResetTimer() 234 for i := 0; i < b.N; i++ { 235 p = p.ByWeight().Reverse() 236 } 237 } 238 239 func setSortVals(dates [4]time.Time, titles [4]string, weights [4]int, pages Pages) { 240 for i := range dates { 241 this := pages[i].(*testPage) 242 other := pages[len(dates)-1-i].(*testPage) 243 244 this.date = dates[i] 245 this.lastMod = dates[i] 246 this.weight = weights[i] 247 this.title = titles[i] 248 // make sure we compare apples and ... apples ... 249 other.linkTitle = this.Title() + "l" 250 other.pubDate = dates[i] 251 other.expiryDate = dates[i] 252 other.content = titles[i] + "_content" 253 } 254 lastLastMod := pages[2].Lastmod() 255 pages[2].(*testPage).lastMod = pages[1].Lastmod() 256 pages[1].(*testPage).lastMod = lastLastMod 257 258 for _, p := range pages { 259 p.(*testPage).content = "" 260 } 261 } 262 263 func createSortTestPages(num int) Pages { 264 pages := make(Pages, num) 265 266 for i := 0; i < num; i++ { 267 p := newTestPage() 268 p.path = fmt.Sprintf("/x/y/p%d.md", i) 269 p.title = fmt.Sprintf("Title %d", i%(num+1/2)) 270 p.params = map[string]any{ 271 "arbitrarily": map[string]any{ 272 "nested": ("xyz" + fmt.Sprintf("%v", 100-i)), 273 }, 274 } 275 276 w := 5 277 278 if i%2 == 0 { 279 w = 10 280 } 281 p.fuzzyWordCount = i 282 p.weight = w 283 p.description = "initial" 284 285 pages[i] = p 286 } 287 288 return pages 289 }