merge_test.go (5859B)
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 collections 15 16 import ( 17 "bytes" 18 "reflect" 19 "testing" 20 21 "github.com/gohugoio/hugo/common/maps" 22 "github.com/gohugoio/hugo/config" 23 "github.com/gohugoio/hugo/deps" 24 "github.com/gohugoio/hugo/langs" 25 "github.com/gohugoio/hugo/parser" 26 "github.com/gohugoio/hugo/parser/metadecoders" 27 28 qt "github.com/frankban/quicktest" 29 ) 30 31 func TestMerge(t *testing.T) { 32 ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())}) 33 34 simpleMap := map[string]any{"a": 1, "b": 2} 35 36 for i, test := range []struct { 37 name string 38 params []any 39 expect any 40 isErr bool 41 }{ 42 { 43 "basic", 44 []any{ 45 map[string]any{"a": 42, "c": 3}, 46 map[string]any{"a": 1, "b": 2}, 47 }, 48 map[string]any{"a": 1, "b": 2, "c": 3}, 49 false, 50 }, 51 { 52 "multi", 53 []any{ 54 map[string]any{"a": 42, "c": 3, "e": 11}, 55 map[string]any{"a": 1, "b": 2}, 56 map[string]any{"a": 9, "c": 4, "d": 7}, 57 }, 58 map[string]any{"a": 9, "b": 2, "c": 4, "d": 7, "e": 11}, 59 false, 60 }, 61 { 62 "basic case insensitive", 63 []any{ 64 map[string]any{"A": 42, "c": 3}, 65 map[string]any{"a": 1, "b": 2}, 66 }, 67 map[string]any{"a": 1, "b": 2, "c": 3}, 68 false, 69 }, 70 { 71 "nested", 72 []any{ 73 map[string]any{"a": 42, "c": 3, "b": map[string]any{"d": 55, "e": 66, "f": 3}}, 74 map[string]any{"a": 1, "b": map[string]any{"d": 1, "e": 2}}, 75 }, 76 map[string]any{"a": 1, "b": map[string]any{"d": 1, "e": 2, "f": 3}, "c": 3}, 77 false, 78 }, 79 { 80 // https://github.com/gohugoio/hugo/issues/6633 81 "params dst", 82 []any{ 83 map[string]any{"a": 42, "c": 3}, 84 maps.Params{"a": 1, "b": 2}, 85 }, 86 maps.Params{"a": int(1), "b": int(2), "c": int(3)}, 87 false, 88 }, 89 { 90 "params dst, upper case src", 91 []any{ 92 map[string]any{"a": 42, "C": 3}, 93 maps.Params{"a": 1, "b": 2}, 94 }, 95 maps.Params{"a": int(1), "b": int(2), "c": int(3)}, 96 false, 97 }, 98 { 99 "params src", 100 []any{ 101 maps.Params{"a": 42, "c": 3}, 102 map[string]any{"a": 1, "c": 2}, 103 }, 104 map[string]any{"a": int(1), "c": int(2)}, 105 false, 106 }, 107 { 108 "params src, upper case dst", 109 []any{ 110 maps.Params{"a": 42, "c": 3}, 111 map[string]any{"a": 1, "C": 2}, 112 }, 113 map[string]any{"a": int(1), "C": int(2)}, 114 false, 115 }, 116 { 117 "nested, params dst", 118 []any{ 119 map[string]any{"a": 42, "c": 3, "b": map[string]any{"d": 55, "e": 66, "f": 3}}, 120 maps.Params{"a": 1, "b": maps.Params{"d": 1, "e": 2}}, 121 }, 122 maps.Params{"a": 1, "b": maps.Params{"d": 1, "e": 2, "f": 3}, "c": 3}, 123 false, 124 }, 125 { 126 // https://github.com/gohugoio/hugo/issues/7899 127 "matching keys with non-map src value", 128 []any{ 129 map[string]any{"k": "v"}, 130 map[string]any{"k": map[string]any{"k2": "v2"}}, 131 }, 132 map[string]any{"k": map[string]any{"k2": "v2"}}, 133 false, 134 }, 135 {"src nil", []any{nil, simpleMap}, simpleMap, false}, 136 // Error cases. 137 {"dst not a map", []any{nil, "not a map"}, nil, true}, 138 {"src not a map", []any{"not a map", simpleMap}, nil, true}, 139 {"different map types", []any{map[int]any{32: "a"}, simpleMap}, nil, true}, 140 {"all nil", []any{nil, nil}, nil, true}, 141 } { 142 143 test := test 144 145 t.Run(test.name, func(t *testing.T) { 146 t.Parallel() 147 errMsg := qt.Commentf("[%d] %v", i, test) 148 149 c := qt.New(t) 150 151 result, err := ns.Merge(test.params...) 152 153 if test.isErr { 154 c.Assert(err, qt.Not(qt.IsNil), errMsg) 155 return 156 } 157 158 c.Assert(err, qt.IsNil) 159 c.Assert(result, qt.DeepEquals, test.expect, errMsg) 160 }) 161 } 162 } 163 164 func TestMergeDataFormats(t *testing.T) { 165 c := qt.New(t) 166 ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())}) 167 168 toml1 := ` 169 V1 = "v1_1" 170 171 [V2s] 172 V21 = "v21_1" 173 174 ` 175 176 toml2 := ` 177 V1 = "v1_2" 178 V2 = "v2_2" 179 180 [V2s] 181 V21 = "v21_2" 182 V22 = "v22_2" 183 184 ` 185 186 meta1, err := metadecoders.Default.UnmarshalToMap([]byte(toml1), metadecoders.TOML) 187 c.Assert(err, qt.IsNil) 188 meta2, err := metadecoders.Default.UnmarshalToMap([]byte(toml2), metadecoders.TOML) 189 c.Assert(err, qt.IsNil) 190 191 for _, format := range []metadecoders.Format{metadecoders.JSON, metadecoders.YAML, metadecoders.TOML} { 192 193 var dataStr1, dataStr2 bytes.Buffer 194 err = parser.InterfaceToConfig(meta1, format, &dataStr1) 195 c.Assert(err, qt.IsNil) 196 err = parser.InterfaceToConfig(meta2, format, &dataStr2) 197 c.Assert(err, qt.IsNil) 198 199 dst, err := metadecoders.Default.UnmarshalToMap(dataStr1.Bytes(), format) 200 c.Assert(err, qt.IsNil) 201 src, err := metadecoders.Default.UnmarshalToMap(dataStr2.Bytes(), format) 202 c.Assert(err, qt.IsNil) 203 204 merged, err := ns.Merge(src, dst) 205 c.Assert(err, qt.IsNil) 206 207 c.Assert( 208 merged, 209 qt.DeepEquals, 210 map[string]any{ 211 "V1": "v1_1", "V2": "v2_2", 212 "V2s": map[string]any{"V21": "v21_1", "V22": "v22_2"}, 213 }) 214 } 215 } 216 217 func TestCaseInsensitiveMapLookup(t *testing.T) { 218 c := qt.New(t) 219 220 m1 := reflect.ValueOf(map[string]any{ 221 "a": 1, 222 "B": 2, 223 }) 224 225 m2 := reflect.ValueOf(map[int]any{ 226 1: 1, 227 2: 2, 228 }) 229 230 var found bool 231 232 a, found := caseInsensitiveLookup(m1, reflect.ValueOf("A")) 233 c.Assert(found, qt.Equals, true) 234 c.Assert(a.Interface(), qt.Equals, 1) 235 236 b, found := caseInsensitiveLookup(m1, reflect.ValueOf("b")) 237 c.Assert(found, qt.Equals, true) 238 c.Assert(b.Interface(), qt.Equals, 2) 239 240 two, found := caseInsensitiveLookup(m2, reflect.ValueOf(2)) 241 c.Assert(found, qt.Equals, true) 242 c.Assert(two.Interface(), qt.Equals, 2) 243 }