hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

sort_test.go (11423B)

    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 	"fmt"
   18 	"reflect"
   19 	"testing"
   20 
   21 	"github.com/gohugoio/hugo/common/maps"
   22 	"github.com/gohugoio/hugo/config"
   23 	"github.com/gohugoio/hugo/langs"
   24 
   25 	"github.com/gohugoio/hugo/deps"
   26 )
   27 
   28 type stringsSlice []string
   29 
   30 func TestSort(t *testing.T) {
   31 	t.Parallel()
   32 
   33 	ns := New(&deps.Deps{
   34 		Language: langs.NewDefaultLanguage(config.New()),
   35 	})
   36 
   37 	type ts struct {
   38 		MyInt    int
   39 		MyFloat  float64
   40 		MyString string
   41 	}
   42 	type mid struct {
   43 		Tst TstX
   44 	}
   45 
   46 	for i, test := range []struct {
   47 		seq         any
   48 		sortByField any
   49 		sortAsc     string
   50 		expect      any
   51 	}{
   52 		{[]string{"class1", "class2", "class3"}, nil, "asc", []string{"class1", "class2", "class3"}},
   53 		{[]string{"class3", "class1", "class2"}, nil, "asc", []string{"class1", "class2", "class3"}},
   54 		{[]string{"CLASS3", "class1", "class2"}, nil, "asc", []string{"class1", "class2", "CLASS3"}},
   55 		// Issue 6023
   56 		{stringsSlice{"class3", "class1", "class2"}, nil, "asc", stringsSlice{"class1", "class2", "class3"}},
   57 
   58 		{[]int{1, 2, 3, 4, 5}, nil, "asc", []int{1, 2, 3, 4, 5}},
   59 		{[]int{5, 4, 3, 1, 2}, nil, "asc", []int{1, 2, 3, 4, 5}},
   60 		// test sort key parameter is forcibly set empty
   61 		{[]string{"class3", "class1", "class2"}, map[int]string{1: "a"}, "asc", []string{"class1", "class2", "class3"}},
   62 		// test map sorting by keys
   63 		{map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, nil, "asc", []int{10, 20, 30, 40, 50}},
   64 		{map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, nil, "asc", []int{30, 20, 10, 40, 50}},
   65 		{map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, nil, "asc", []string{"10", "20", "30", "40", "50"}},
   66 		{map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, nil, "asc", []string{"30", "20", "10", "40", "50"}},
   67 		{map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, nil, "asc", []string{"50", "40", "10", "30", "20"}},
   68 		{map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, nil, "asc", []string{"10", "20", "30", "40", "50"}},
   69 		{map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, nil, "asc", []string{"30", "20", "10", "40", "50"}},
   70 		{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"}},
   71 		// test map sorting by value
   72 		{map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "value", "asc", []int{10, 20, 30, 40, 50}},
   73 		{map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "value", "asc", []int{10, 20, 30, 40, 50}},
   74 		// test map sorting by field value
   75 		{
   76 			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"}},
   77 			"MyInt",
   78 			"asc",
   79 			[]ts{{10, 10.5, "ten"}, {20, 20.5, "twenty"}, {30, 30.5, "thirty"}, {40, 40.5, "forty"}, {50, 50.5, "fifty"}},
   80 		},
   81 		{
   82 			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"}},
   83 			"MyFloat",
   84 			"asc",
   85 			[]ts{{10, 10.5, "ten"}, {20, 20.5, "twenty"}, {30, 30.5, "thirty"}, {40, 40.5, "forty"}, {50, 50.5, "fifty"}},
   86 		},
   87 		{
   88 			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"}},
   89 			"MyString",
   90 			"asc",
   91 			[]ts{{50, 50.5, "fifty"}, {40, 40.5, "forty"}, {10, 10.5, "ten"}, {30, 30.5, "thirty"}, {20, 20.5, "twenty"}},
   92 		},
   93 		// test sort desc
   94 		{[]string{"class1", "class2", "class3"}, "value", "desc", []string{"class3", "class2", "class1"}},
   95 		{[]string{"class3", "class1", "class2"}, "value", "desc", []string{"class3", "class2", "class1"}},
   96 		// test sort by struct's method
   97 		{
   98 			[]TstX{{A: "i", B: "j"}, {A: "e", B: "f"}, {A: "c", B: "d"}, {A: "g", B: "h"}, {A: "a", B: "b"}},
   99 			"TstRv",
  100 			"asc",
  101 			[]TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}},
  102 		},
  103 		{
  104 			[]*TstX{{A: "i", B: "j"}, {A: "e", B: "f"}, {A: "c", B: "d"}, {A: "g", B: "h"}, {A: "a", B: "b"}},
  105 			"TstRp",
  106 			"asc",
  107 			[]*TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}},
  108 		},
  109 		// Lower case Params, slice
  110 		{
  111 			[]TstParams{{params: maps.Params{"color": "indigo"}}, {params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}},
  112 			".Params.COLOR",
  113 			"asc",
  114 			[]TstParams{{params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}, {params: maps.Params{"color": "indigo"}}},
  115 		},
  116 		// Lower case Params, map
  117 		{
  118 			map[string]TstParams{"1": {params: maps.Params{"color": "indigo"}}, "2": {params: maps.Params{"color": "blue"}}, "3": {params: maps.Params{"color": "green"}}},
  119 			".Params.CoLoR",
  120 			"asc",
  121 			[]TstParams{{params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}, {params: maps.Params{"color": "indigo"}}},
  122 		},
  123 		// test map sorting by struct's method
  124 		{
  125 			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"}},
  126 			"TstRv",
  127 			"asc",
  128 			[]TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}},
  129 		},
  130 		{
  131 			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"}},
  132 			"TstRp",
  133 			"asc",
  134 			[]*TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}},
  135 		},
  136 		// test sort by dot chaining key argument
  137 		{
  138 			[]map[string]TstX{{"foo": TstX{A: "e", B: "f"}}, {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}},
  139 			"foo.A",
  140 			"asc",
  141 			[]map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}},
  142 		},
  143 		{
  144 			[]map[string]TstX{{"foo": TstX{A: "e", B: "f"}}, {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}},
  145 			".foo.A",
  146 			"asc",
  147 			[]map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}},
  148 		},
  149 		{
  150 			[]map[string]TstX{{"foo": TstX{A: "e", B: "f"}}, {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}},
  151 			"foo.TstRv",
  152 			"asc",
  153 			[]map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}},
  154 		},
  155 		{
  156 			[]map[string]*TstX{{"foo": &TstX{A: "e", B: "f"}}, {"foo": &TstX{A: "a", B: "b"}}, {"foo": &TstX{A: "c", B: "d"}}},
  157 			"foo.TstRp",
  158 			"asc",
  159 			[]map[string]*TstX{{"foo": &TstX{A: "a", B: "b"}}, {"foo": &TstX{A: "c", B: "d"}}, {"foo": &TstX{A: "e", B: "f"}}},
  160 		},
  161 		{
  162 			[]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"}}}},
  163 			"foo.Tst.A",
  164 			"asc",
  165 			[]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"}}}},
  166 		},
  167 		{
  168 			[]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"}}}},
  169 			"foo.Tst.TstRv",
  170 			"asc",
  171 			[]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"}}}},
  172 		},
  173 		// test map sorting by dot chaining key argument
  174 		{
  175 			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"}}},
  176 			"foo.A",
  177 			"asc",
  178 			[]map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}},
  179 		},
  180 		{
  181 			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"}}},
  182 			".foo.A",
  183 			"asc",
  184 			[]map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}},
  185 		},
  186 		{
  187 			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"}}},
  188 			"foo.TstRv",
  189 			"asc",
  190 			[]map[string]TstX{{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}}},
  191 		},
  192 		{
  193 			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"}}},
  194 			"foo.TstRp",
  195 			"asc",
  196 			[]map[string]*TstX{{"foo": &TstX{A: "a", B: "b"}}, {"foo": &TstX{A: "c", B: "d"}}, {"foo": &TstX{A: "e", B: "f"}}},
  197 		},
  198 		{
  199 			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"}}}},
  200 			"foo.Tst.A",
  201 			"asc",
  202 			[]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"}}}},
  203 		},
  204 		{
  205 			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"}}}},
  206 			"foo.Tst.TstRv",
  207 			"asc",
  208 			[]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"}}}},
  209 		},
  210 		// interface slice with missing elements
  211 		{
  212 			[]any{
  213 				map[any]any{"Title": "Foo", "Weight": 10},
  214 				map[any]any{"Title": "Bar"},
  215 				map[any]any{"Title": "Zap", "Weight": 5},
  216 			},
  217 			"Weight",
  218 			"asc",
  219 			[]any{
  220 				map[any]any{"Title": "Bar"},
  221 				map[any]any{"Title": "Zap", "Weight": 5},
  222 				map[any]any{"Title": "Foo", "Weight": 10},
  223 			},
  224 		},
  225 		// test boolean values
  226 		{[]bool{false, true, false}, "value", "asc", []bool{false, false, true}},
  227 		{[]bool{false, true, false}, "value", "desc", []bool{true, false, false}},
  228 		// test error cases
  229 		{(*[]TstX)(nil), nil, "asc", false},
  230 		{TstX{A: "a", B: "b"}, nil, "asc", false},
  231 		{
  232 			[]map[string]TstX{{"foo": TstX{A: "e", B: "f"}}, {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}},
  233 			"foo.NotAvailable",
  234 			"asc",
  235 			false,
  236 		},
  237 		{
  238 			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"}}},
  239 			"foo.NotAvailable",
  240 			"asc",
  241 			false,
  242 		},
  243 		{nil, nil, "asc", false},
  244 	} {
  245 		t.Run(fmt.Sprintf("test%d", i), func(t *testing.T) {
  246 			var result any
  247 			var err error
  248 			if test.sortByField == nil {
  249 				result, err = ns.Sort(test.seq)
  250 			} else {
  251 				result, err = ns.Sort(test.seq, test.sortByField, test.sortAsc)
  252 			}
  253 
  254 			if b, ok := test.expect.(bool); ok && !b {
  255 				if err == nil {
  256 					t.Fatal("Sort didn't return an expected error")
  257 				}
  258 			} else {
  259 				if err != nil {
  260 					t.Fatalf("failed: %s", err)
  261 				}
  262 				if !reflect.DeepEqual(result, test.expect) {
  263 					t.Fatalf("Sort called on sequence: %#v | sortByField: `%v` | got\n%#v but expected\n%#v", test.seq, test.sortByField, result, test.expect)
  264 				}
  265 			}
  266 		})
  267 	}
  268 }