github.com/rezahousseini/hugo@v0.32.3/parser/frontmatter_test.go (about) 1 // Copyright 2015 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 parser 15 16 import ( 17 "bytes" 18 "fmt" 19 "reflect" 20 "strings" 21 "testing" 22 ) 23 24 func TestInterfaceToConfig(t *testing.T) { 25 cases := []struct { 26 input interface{} 27 mark byte 28 want []byte 29 isErr bool 30 }{ 31 // TOML 32 {map[string]interface{}{}, TOMLLead[0], nil, false}, 33 { 34 map[string]interface{}{"title": "test 1"}, 35 TOMLLead[0], 36 []byte("title = \"test 1\"\n"), 37 false, 38 }, 39 40 // YAML 41 {map[string]interface{}{}, YAMLLead[0], []byte("{}\n"), false}, 42 { 43 map[string]interface{}{"title": "test 1"}, 44 YAMLLead[0], 45 []byte("title: test 1\n"), 46 false, 47 }, 48 49 // JSON 50 {map[string]interface{}{}, JSONLead[0], []byte("{}\n"), false}, 51 { 52 map[string]interface{}{"title": "test 1"}, 53 JSONLead[0], 54 []byte("{\n \"title\": \"test 1\"\n}\n"), 55 false, 56 }, 57 58 // Errors 59 {nil, TOMLLead[0], nil, true}, 60 {map[string]interface{}{}, '$', nil, true}, 61 } 62 63 for i, c := range cases { 64 var buf bytes.Buffer 65 66 err := InterfaceToConfig(c.input, rune(c.mark), &buf) 67 if err != nil { 68 if c.isErr { 69 continue 70 } 71 t.Fatalf("[%d] unexpected error value: %v", i, err) 72 } 73 74 if !reflect.DeepEqual(buf.Bytes(), c.want) { 75 t.Errorf("[%d] not equal:\nwant %q,\n got %q", i, c.want, buf.Bytes()) 76 } 77 } 78 } 79 80 func TestInterfaceToFrontMatter(t *testing.T) { 81 cases := []struct { 82 input interface{} 83 mark rune 84 want []byte 85 isErr bool 86 }{ 87 // TOML 88 {map[string]interface{}{}, '+', []byte("+++\n\n+++\n"), false}, 89 { 90 map[string]interface{}{"title": "test 1"}, 91 '+', 92 []byte("+++\ntitle = \"test 1\"\n\n+++\n"), 93 false, 94 }, 95 96 // YAML 97 {map[string]interface{}{}, '-', []byte("---\n{}\n---\n"), false}, // 98 { 99 map[string]interface{}{"title": "test 1"}, 100 '-', 101 []byte("---\ntitle: test 1\n---\n"), 102 false, 103 }, 104 105 // JSON 106 {map[string]interface{}{}, '{', []byte("{}\n"), false}, 107 { 108 map[string]interface{}{"title": "test 1"}, 109 '{', 110 []byte("{\n \"title\": \"test 1\"\n}\n"), 111 false, 112 }, 113 114 // Errors 115 {nil, '+', nil, true}, 116 {map[string]interface{}{}, '$', nil, true}, 117 } 118 119 for i, c := range cases { 120 var buf bytes.Buffer 121 err := InterfaceToFrontMatter(c.input, c.mark, &buf) 122 if err != nil { 123 if c.isErr { 124 continue 125 } 126 t.Fatalf("[%d] unexpected error value: %v", i, err) 127 } 128 129 if !reflect.DeepEqual(buf.Bytes(), c.want) { 130 t.Errorf("[%d] not equal:\nwant %q,\n got %q", i, c.want, buf.Bytes()) 131 } 132 } 133 } 134 135 func TestHandleTOMLMetaData(t *testing.T) { 136 cases := []struct { 137 input []byte 138 want interface{} 139 isErr bool 140 }{ 141 {nil, map[string]interface{}{}, false}, 142 {[]byte("title = \"test 1\""), map[string]interface{}{"title": "test 1"}, false}, 143 {[]byte("a = [1, 2, 3]"), map[string]interface{}{"a": []interface{}{int64(1), int64(2), int64(3)}}, false}, 144 {[]byte("b = [\n[1, 2],\n[3, 4]\n]"), map[string]interface{}{"b": []interface{}{[]interface{}{int64(1), int64(2)}, []interface{}{int64(3), int64(4)}}}, false}, 145 // errors 146 {[]byte("z = [\n[1, 2]\n[3, 4]\n]"), nil, true}, 147 } 148 149 for i, c := range cases { 150 res, err := HandleTOMLMetaData(c.input) 151 if err != nil { 152 if c.isErr { 153 continue 154 } 155 t.Fatalf("[%d] unexpected error value: %v", i, err) 156 } 157 158 if !reflect.DeepEqual(res, c.want) { 159 t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res) 160 } 161 } 162 } 163 164 func TestHandleYAMLMetaData(t *testing.T) { 165 cases := []struct { 166 input []byte 167 want interface{} 168 isErr bool 169 }{ 170 {nil, map[string]interface{}{}, false}, 171 {[]byte("title: test 1"), map[string]interface{}{"title": "test 1"}, false}, 172 {[]byte("a: Easy!\nb:\n c: 2\n d: [3, 4]"), map[string]interface{}{"a": "Easy!", "b": map[interface{}]interface{}{"c": 2, "d": []interface{}{3, 4}}}, false}, 173 // errors 174 {[]byte("z = not toml"), nil, true}, 175 } 176 177 for i, c := range cases { 178 res, err := HandleYAMLMetaData(c.input) 179 if err != nil { 180 if c.isErr { 181 continue 182 } 183 t.Fatalf("[%d] unexpected error value: %v", i, err) 184 } 185 186 if !reflect.DeepEqual(res, c.want) { 187 t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res) 188 } 189 } 190 } 191 192 func TestHandleJSONMetaData(t *testing.T) { 193 cases := []struct { 194 input []byte 195 want interface{} 196 isErr bool 197 }{ 198 {nil, map[string]interface{}{}, false}, 199 {[]byte("{\"title\": \"test 1\"}"), map[string]interface{}{"title": "test 1"}, false}, 200 // errors 201 {[]byte("{noquotes}"), nil, true}, 202 } 203 204 for i, c := range cases { 205 res, err := HandleJSONMetaData(c.input) 206 if err != nil { 207 if c.isErr { 208 continue 209 } 210 t.Fatalf("[%d] unexpected error value: %v", i, err) 211 } 212 213 if !reflect.DeepEqual(res, c.want) { 214 t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res) 215 } 216 } 217 } 218 219 func TestHandleOrgMetaData(t *testing.T) { 220 cases := []struct { 221 input []byte 222 want interface{} 223 isErr bool 224 }{ 225 {nil, map[string]interface{}{}, false}, 226 {[]byte("#+title: test 1\n"), map[string]interface{}{"title": "test 1"}, false}, 227 } 228 229 for i, c := range cases { 230 res, err := HandleOrgMetaData(c.input) 231 if err != nil { 232 if c.isErr { 233 continue 234 } 235 t.Fatalf("[%d] unexpected error value: %v", i, err) 236 } 237 238 if !reflect.DeepEqual(res, c.want) { 239 t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res) 240 } 241 } 242 } 243 244 func TestFormatToLeadRune(t *testing.T) { 245 for i, this := range []struct { 246 kind string 247 expect rune 248 }{ 249 {"yaml", '-'}, 250 {"yml", '-'}, 251 {"toml", '+'}, 252 {"tml", '+'}, 253 {"json", '{'}, 254 {"js", '{'}, 255 {"org", '#'}, 256 {"unknown", '+'}, 257 } { 258 result := FormatToLeadRune(this.kind) 259 260 if result != this.expect { 261 t.Errorf("[%d] got %q but expected %q", i, result, this.expect) 262 } 263 } 264 } 265 266 func TestDetectFrontMatter(t *testing.T) { 267 cases := []struct { 268 mark rune 269 want *FrontmatterType 270 }{ 271 // funcs are uncomparable, so we ignore FrontmatterType.Parse in these tests 272 {'-', &FrontmatterType{nil, []byte(YAMLDelim), []byte(YAMLDelim), false}}, 273 {'+', &FrontmatterType{nil, []byte(TOMLDelim), []byte(TOMLDelim), false}}, 274 {'{', &FrontmatterType{nil, []byte("{"), []byte("}"), true}}, 275 {'#', &FrontmatterType{nil, []byte("#+"), []byte("\n"), false}}, 276 {'$', nil}, 277 } 278 279 for _, c := range cases { 280 res := DetectFrontMatter(c.mark) 281 if res == nil { 282 if c.want == nil { 283 continue 284 } 285 286 t.Fatalf("want %v, got %v", *c.want, res) 287 } 288 289 if !reflect.DeepEqual(res.markstart, c.want.markstart) { 290 t.Errorf("markstart mismatch: want %v, got %v", c.want.markstart, res.markstart) 291 } 292 if !reflect.DeepEqual(res.markend, c.want.markend) { 293 t.Errorf("markend mismatch: want %v, got %v", c.want.markend, res.markend) 294 } 295 if !reflect.DeepEqual(res.includeMark, c.want.includeMark) { 296 t.Errorf("includeMark mismatch: want %v, got %v", c.want.includeMark, res.includeMark) 297 } 298 } 299 } 300 301 func TestRemoveTOMLIdentifier(t *testing.T) { 302 cases := []struct { 303 input string 304 want string 305 }{ 306 {"a = 1", "a = 1"}, 307 {"a = 1\r\n", "a = 1\r\n"}, 308 {"+++\r\na = 1\r\n+++\r\n", "a = 1\r\n"}, 309 {"+++\na = 1\n+++\n", "a = 1\n"}, 310 {"+++\nb = \"+++ oops +++\"\n+++\n", "b = \"+++ oops +++\"\n"}, 311 {"+++\nc = \"\"\"+++\noops\n+++\n\"\"\"\"\n+++\n", "c = \"\"\"+++\noops\n+++\n\"\"\"\"\n"}, 312 {"+++\nd = 1\n+++", "d = 1\n"}, 313 } 314 315 for i, c := range cases { 316 res := removeTOMLIdentifier([]byte(c.input)) 317 if string(res) != c.want { 318 t.Errorf("[%d] given %q\nwant: %q\n got: %q", i, c.input, c.want, res) 319 } 320 } 321 } 322 323 func BenchmarkFrontmatterTags(b *testing.B) { 324 325 for _, frontmatter := range []string{"JSON", "YAML", "YAML2", "TOML"} { 326 for i := 1; i < 60; i += 20 { 327 doBenchmarkFrontmatter(b, frontmatter, i) 328 } 329 } 330 } 331 332 func doBenchmarkFrontmatter(b *testing.B, fileformat string, numTags int) { 333 yamlTemplate := `--- 334 name: "Tags" 335 tags: 336 %s 337 --- 338 ` 339 340 yaml2Template := `--- 341 name: "Tags" 342 tags: %s 343 --- 344 ` 345 tomlTemplate := `+++ 346 name = "Tags" 347 tags = %s 348 +++ 349 ` 350 351 jsonTemplate := `{ 352 "name": "Tags", 353 "tags": [ 354 %s 355 ] 356 }` 357 name := fmt.Sprintf("%s:%d", fileformat, numTags) 358 b.Run(name, func(b *testing.B) { 359 tags := make([]string, numTags) 360 var ( 361 tagsStr string 362 frontmatterTemplate string 363 ) 364 for i := 0; i < numTags; i++ { 365 tags[i] = fmt.Sprintf("Hugo %d", i+1) 366 } 367 if fileformat == "TOML" { 368 frontmatterTemplate = tomlTemplate 369 tagsStr = strings.Replace(fmt.Sprintf("%q", tags), " ", ", ", -1) 370 } else if fileformat == "JSON" { 371 frontmatterTemplate = jsonTemplate 372 tagsStr = strings.Replace(fmt.Sprintf("%q", tags), " ", ", ", -1) 373 } else if fileformat == "YAML2" { 374 frontmatterTemplate = yaml2Template 375 tagsStr = strings.Replace(fmt.Sprintf("%q", tags), " ", ", ", -1) 376 } else { 377 frontmatterTemplate = yamlTemplate 378 for _, tag := range tags { 379 tagsStr += "\n- " + tag 380 } 381 } 382 383 frontmatter := fmt.Sprintf(frontmatterTemplate, tagsStr) 384 385 p := page{frontmatter: []byte(frontmatter)} 386 387 b.ResetTimer() 388 for i := 0; i < b.N; i++ { 389 meta, err := p.Metadata() 390 if err != nil { 391 b.Fatal(err) 392 } 393 if meta == nil { 394 b.Fatal("Meta is nil") 395 } 396 } 397 }) 398 }