github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/helpers/path_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 helpers 15 16 import ( 17 "fmt" 18 "io/ioutil" 19 "os" 20 "path/filepath" 21 "reflect" 22 "runtime" 23 "strconv" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/gohugoio/hugo/langs" 29 30 qt "github.com/frankban/quicktest" 31 32 "github.com/gohugoio/hugo/hugofs" 33 "github.com/spf13/afero" 34 ) 35 36 func TestMakePath(t *testing.T) { 37 c := qt.New(t) 38 tests := []struct { 39 input string 40 expected string 41 removeAccents bool 42 }{ 43 {"dot.slash/backslash\\underscore_pound#plus+hyphen-", "dot.slash/backslash\\underscore_pound#plus+hyphen-", true}, 44 {"abcXYZ0123456789", "abcXYZ0123456789", true}, 45 {"%20 %2", "%20-2", true}, 46 {"foo- bar", "foo-bar", true}, 47 {" Foo bar ", "Foo-bar", true}, 48 {"Foo.Bar/foo_Bar-Foo", "Foo.Bar/foo_Bar-Foo", true}, 49 {"fOO,bar:foobAR", "fOObarfoobAR", true}, 50 {"FOo/BaR.html", "FOo/BaR.html", true}, 51 {"трям/трям", "трям/трям", true}, 52 {"은행", "은행", true}, 53 {"Банковский кассир", "Банковскии-кассир", true}, 54 // Issue #1488 55 {"संस्कृत", "संस्कृत", false}, 56 {"a%C3%B1ame", "a%C3%B1ame", false}, // Issue #1292 57 {"this+is+a+test", "this+is+a+test", false}, // Issue #1290 58 {"~foo", "~foo", false}, // Issue #2177 59 {"foo--bar", "foo--bar", true}, // Issue #7288 60 } 61 62 for _, test := range tests { 63 v := newTestCfg() 64 v.Set("removePathAccents", test.removeAccents) 65 66 l := langs.NewDefaultLanguage(v) 67 p, err := NewPathSpec(hugofs.NewMem(v), l, nil) 68 c.Assert(err, qt.IsNil) 69 70 output := p.MakePath(test.input) 71 if output != test.expected { 72 t.Errorf("Expected %#v, got %#v\n", test.expected, output) 73 } 74 } 75 } 76 77 func TestMakePathSanitized(t *testing.T) { 78 v := newTestCfg() 79 80 p, _ := NewPathSpec(hugofs.NewMem(v), v, nil) 81 82 tests := []struct { 83 input string 84 expected string 85 }{ 86 {" FOO bar ", "foo-bar"}, 87 {"Foo.Bar/fOO_bAr-Foo", "foo.bar/foo_bar-foo"}, 88 {"FOO,bar:FooBar", "foobarfoobar"}, 89 {"foo/BAR.HTML", "foo/bar.html"}, 90 {"трям/трям", "трям/трям"}, 91 {"은행", "은행"}, 92 } 93 94 for _, test := range tests { 95 output := p.MakePathSanitized(test.input) 96 if output != test.expected { 97 t.Errorf("Expected %#v, got %#v\n", test.expected, output) 98 } 99 } 100 } 101 102 func TestMakePathSanitizedDisablePathToLower(t *testing.T) { 103 v := newTestCfg() 104 105 v.Set("disablePathToLower", true) 106 107 l := langs.NewDefaultLanguage(v) 108 p, _ := NewPathSpec(hugofs.NewMem(v), l, nil) 109 110 tests := []struct { 111 input string 112 expected string 113 }{ 114 {" FOO bar ", "FOO-bar"}, 115 {"Foo.Bar/fOO_bAr-Foo", "Foo.Bar/fOO_bAr-Foo"}, 116 {"FOO,bar:FooBar", "FOObarFooBar"}, 117 {"foo/BAR.HTML", "foo/BAR.HTML"}, 118 {"трям/трям", "трям/трям"}, 119 {"은행", "은행"}, 120 } 121 122 for _, test := range tests { 123 output := p.MakePathSanitized(test.input) 124 if output != test.expected { 125 t.Errorf("Expected %#v, got %#v\n", test.expected, output) 126 } 127 } 128 } 129 130 func TestMakePathRelative(t *testing.T) { 131 type test struct { 132 inPath, path1, path2, output string 133 } 134 135 data := []test{ 136 {"/abc/bcd/ab.css", "/abc/bcd", "/bbc/bcd", "/ab.css"}, 137 {"/abc/bcd/ab.css", "/abcd/bcd", "/abc/bcd", "/ab.css"}, 138 } 139 140 for i, d := range data { 141 output, _ := makePathRelative(d.inPath, d.path1, d.path2) 142 if d.output != output { 143 t.Errorf("Test #%d failed. Expected %q got %q", i, d.output, output) 144 } 145 } 146 _, error := makePathRelative("a/b/c.ss", "/a/c", "/d/c", "/e/f") 147 148 if error == nil { 149 t.Errorf("Test failed, expected error") 150 } 151 } 152 153 func TestGetDottedRelativePath(t *testing.T) { 154 // on Windows this will receive both kinds, both country and western ... 155 for _, f := range []func(string) string{filepath.FromSlash, func(s string) string { return s }} { 156 doTestGetDottedRelativePath(f, t) 157 } 158 } 159 160 func doTestGetDottedRelativePath(urlFixer func(string) string, t *testing.T) { 161 type test struct { 162 input, expected string 163 } 164 data := []test{ 165 {"", "./"}, 166 {urlFixer("/"), "./"}, 167 {urlFixer("post"), "../"}, 168 {urlFixer("/post"), "../"}, 169 {urlFixer("post/"), "../"}, 170 {urlFixer("tags/foo.html"), "../"}, 171 {urlFixer("/tags/foo.html"), "../"}, 172 {urlFixer("/post/"), "../"}, 173 {urlFixer("////post/////"), "../"}, 174 {urlFixer("/foo/bar/index.html"), "../../"}, 175 {urlFixer("/foo/bar/foo/"), "../../../"}, 176 {urlFixer("/foo/bar/foo"), "../../../"}, 177 {urlFixer("foo/bar/foo/"), "../../../"}, 178 {urlFixer("foo/bar/foo/bar"), "../../../../"}, 179 {"404.html", "./"}, 180 {"404.xml", "./"}, 181 {"/404.html", "./"}, 182 } 183 for i, d := range data { 184 output := GetDottedRelativePath(d.input) 185 if d.expected != output { 186 t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output) 187 } 188 } 189 } 190 191 func TestMakeTitle(t *testing.T) { 192 type test struct { 193 input, expected string 194 } 195 data := []test{ 196 {"Make-Title", "Make Title"}, 197 {"MakeTitle", "MakeTitle"}, 198 {"make_title", "make_title"}, 199 } 200 for i, d := range data { 201 output := MakeTitle(d.input) 202 if d.expected != output { 203 t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output) 204 } 205 } 206 } 207 208 func TestDirExists(t *testing.T) { 209 type test struct { 210 input string 211 expected bool 212 } 213 214 data := []test{ 215 {".", true}, 216 {"./", true}, 217 {"..", true}, 218 {"../", true}, 219 {"./..", true}, 220 {"./../", true}, 221 {os.TempDir(), true}, 222 {os.TempDir() + FilePathSeparator, true}, 223 {"/", true}, 224 {"/some-really-random-directory-name", false}, 225 {"/some/really/random/directory/name", false}, 226 {"./some-really-random-local-directory-name", false}, 227 {"./some/really/random/local/directory/name", false}, 228 } 229 230 for i, d := range data { 231 exists, _ := DirExists(filepath.FromSlash(d.input), new(afero.OsFs)) 232 if d.expected != exists { 233 t.Errorf("Test %d failed. Expected %t got %t", i, d.expected, exists) 234 } 235 } 236 } 237 238 func TestIsDir(t *testing.T) { 239 type test struct { 240 input string 241 expected bool 242 } 243 data := []test{ 244 {"./", true}, 245 {"/", true}, 246 {"./this-directory-does-not-existi", false}, 247 {"/this-absolute-directory/does-not-exist", false}, 248 } 249 250 for i, d := range data { 251 252 exists, _ := IsDir(d.input, new(afero.OsFs)) 253 if d.expected != exists { 254 t.Errorf("Test %d failed. Expected %t got %t", i, d.expected, exists) 255 } 256 } 257 } 258 259 func TestIsEmpty(t *testing.T) { 260 zeroSizedFile, _ := createZeroSizedFileInTempDir() 261 defer deleteFileInTempDir(zeroSizedFile) 262 nonZeroSizedFile, _ := createNonZeroSizedFileInTempDir() 263 defer deleteFileInTempDir(nonZeroSizedFile) 264 emptyDirectory, _ := createEmptyTempDir() 265 defer deleteTempDir(emptyDirectory) 266 nonEmptyZeroLengthFilesDirectory, _ := createTempDirWithZeroLengthFiles() 267 defer deleteTempDir(nonEmptyZeroLengthFilesDirectory) 268 nonEmptyNonZeroLengthFilesDirectory, _ := createTempDirWithNonZeroLengthFiles() 269 defer deleteTempDir(nonEmptyNonZeroLengthFilesDirectory) 270 nonExistentFile := os.TempDir() + "/this-file-does-not-exist.txt" 271 nonExistentDir := os.TempDir() + "/this/directory/does/not/exist/" 272 273 fileDoesNotExist := fmt.Errorf("%q path does not exist", nonExistentFile) 274 dirDoesNotExist := fmt.Errorf("%q path does not exist", nonExistentDir) 275 276 type test struct { 277 input string 278 expectedResult bool 279 expectedErr error 280 } 281 282 data := []test{ 283 {zeroSizedFile.Name(), true, nil}, 284 {nonZeroSizedFile.Name(), false, nil}, 285 {emptyDirectory, true, nil}, 286 {nonEmptyZeroLengthFilesDirectory, false, nil}, 287 {nonEmptyNonZeroLengthFilesDirectory, false, nil}, 288 {nonExistentFile, false, fileDoesNotExist}, 289 {nonExistentDir, false, dirDoesNotExist}, 290 } 291 for i, d := range data { 292 exists, err := IsEmpty(d.input, new(afero.OsFs)) 293 if d.expectedResult != exists { 294 t.Errorf("Test %d failed. Expected result %t got %t", i, d.expectedResult, exists) 295 } 296 if d.expectedErr != nil { 297 if d.expectedErr.Error() != err.Error() { 298 t.Errorf("Test %d failed. Expected %q(%#v) got %q(%#v)", i, d.expectedErr, d.expectedErr, err, err) 299 } 300 } else { 301 if d.expectedErr != err { 302 t.Errorf("Test %d failed. Expected %q(%#v) got %q(%#v)", i, d.expectedErr, d.expectedErr, err, err) 303 } 304 } 305 } 306 } 307 308 func createZeroSizedFileInTempDir() (*os.File, error) { 309 filePrefix := "_path_test_" 310 f, e := ioutil.TempFile("", filePrefix) // dir is os.TempDir() 311 if e != nil { 312 // if there was an error no file was created. 313 // => no requirement to delete the file 314 return nil, e 315 } 316 return f, nil 317 } 318 319 func createNonZeroSizedFileInTempDir() (*os.File, error) { 320 f, err := createZeroSizedFileInTempDir() 321 if err != nil { 322 // no file ?? 323 return nil, err 324 } 325 byteString := []byte("byteString") 326 err = ioutil.WriteFile(f.Name(), byteString, 0644) 327 if err != nil { 328 // delete the file 329 deleteFileInTempDir(f) 330 return nil, err 331 } 332 return f, nil 333 } 334 335 func deleteFileInTempDir(f *os.File) { 336 _ = os.Remove(f.Name()) 337 } 338 339 func createEmptyTempDir() (string, error) { 340 dirPrefix := "_dir_prefix_" 341 d, e := ioutil.TempDir("", dirPrefix) // will be in os.TempDir() 342 if e != nil { 343 // no directory to delete - it was never created 344 return "", e 345 } 346 return d, nil 347 } 348 349 func createTempDirWithZeroLengthFiles() (string, error) { 350 d, dirErr := createEmptyTempDir() 351 if dirErr != nil { 352 return "", dirErr 353 } 354 filePrefix := "_path_test_" 355 _, fileErr := ioutil.TempFile(d, filePrefix) // dir is os.TempDir() 356 if fileErr != nil { 357 // if there was an error no file was created. 358 // but we need to remove the directory to clean-up 359 deleteTempDir(d) 360 return "", fileErr 361 } 362 // the dir now has one, zero length file in it 363 return d, nil 364 } 365 366 func createTempDirWithNonZeroLengthFiles() (string, error) { 367 d, dirErr := createEmptyTempDir() 368 if dirErr != nil { 369 return "", dirErr 370 } 371 filePrefix := "_path_test_" 372 f, fileErr := ioutil.TempFile(d, filePrefix) // dir is os.TempDir() 373 if fileErr != nil { 374 // if there was an error no file was created. 375 // but we need to remove the directory to clean-up 376 deleteTempDir(d) 377 return "", fileErr 378 } 379 byteString := []byte("byteString") 380 381 fileErr = ioutil.WriteFile(f.Name(), byteString, 0644) 382 if fileErr != nil { 383 // delete the file 384 deleteFileInTempDir(f) 385 // also delete the directory 386 deleteTempDir(d) 387 return "", fileErr 388 } 389 390 // the dir now has one, zero length file in it 391 return d, nil 392 } 393 394 func deleteTempDir(d string) { 395 _ = os.RemoveAll(d) 396 } 397 398 func TestExists(t *testing.T) { 399 zeroSizedFile, _ := createZeroSizedFileInTempDir() 400 defer deleteFileInTempDir(zeroSizedFile) 401 nonZeroSizedFile, _ := createNonZeroSizedFileInTempDir() 402 defer deleteFileInTempDir(nonZeroSizedFile) 403 emptyDirectory, _ := createEmptyTempDir() 404 defer deleteTempDir(emptyDirectory) 405 nonExistentFile := os.TempDir() + "/this-file-does-not-exist.txt" 406 nonExistentDir := os.TempDir() + "/this/directory/does/not/exist/" 407 408 type test struct { 409 input string 410 expectedResult bool 411 expectedErr error 412 } 413 414 data := []test{ 415 {zeroSizedFile.Name(), true, nil}, 416 {nonZeroSizedFile.Name(), true, nil}, 417 {emptyDirectory, true, nil}, 418 {nonExistentFile, false, nil}, 419 {nonExistentDir, false, nil}, 420 } 421 for i, d := range data { 422 exists, err := Exists(d.input, new(afero.OsFs)) 423 if d.expectedResult != exists { 424 t.Errorf("Test %d failed. Expected result %t got %t", i, d.expectedResult, exists) 425 } 426 if d.expectedErr != err { 427 t.Errorf("Test %d failed. Expected %q got %q", i, d.expectedErr, err) 428 } 429 } 430 } 431 432 func TestAbsPathify(t *testing.T) { 433 type test struct { 434 inPath, workingDir, expected string 435 } 436 data := []test{ 437 {os.TempDir(), filepath.FromSlash("/work"), filepath.Clean(os.TempDir())}, // TempDir has trailing slash 438 {"dir", filepath.FromSlash("/work"), filepath.FromSlash("/work/dir")}, 439 } 440 441 windowsData := []test{ 442 {"c:\\banana\\..\\dir", "c:\\foo", "c:\\dir"}, 443 {"\\dir", "c:\\foo", "c:\\foo\\dir"}, 444 {"c:\\", "c:\\foo", "c:\\"}, 445 } 446 447 unixData := []test{ 448 {"/banana/../dir/", "/work", "/dir"}, 449 } 450 451 for i, d := range data { 452 // todo see comment in AbsPathify 453 ps := newTestDefaultPathSpec("workingDir", d.workingDir) 454 455 expected := ps.AbsPathify(d.inPath) 456 if d.expected != expected { 457 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected) 458 } 459 } 460 t.Logf("Running platform specific path tests for %s", runtime.GOOS) 461 if runtime.GOOS == "windows" { 462 for i, d := range windowsData { 463 ps := newTestDefaultPathSpec("workingDir", d.workingDir) 464 465 expected := ps.AbsPathify(d.inPath) 466 if d.expected != expected { 467 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected) 468 } 469 } 470 } else { 471 for i, d := range unixData { 472 ps := newTestDefaultPathSpec("workingDir", d.workingDir) 473 474 expected := ps.AbsPathify(d.inPath) 475 if d.expected != expected { 476 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected) 477 } 478 } 479 } 480 } 481 482 func TestExtractAndGroupRootPaths(t *testing.T) { 483 in := []string{ 484 filepath.FromSlash("/a/b/c/d"), 485 filepath.FromSlash("/a/b/c/e"), 486 filepath.FromSlash("/a/b/e/f"), 487 filepath.FromSlash("/a/b"), 488 filepath.FromSlash("/a/b/c/b/g"), 489 filepath.FromSlash("/c/d/e"), 490 } 491 492 inCopy := make([]string, len(in)) 493 copy(inCopy, in) 494 495 result := ExtractAndGroupRootPaths(in) 496 497 c := qt.New(t) 498 c.Assert(fmt.Sprint(result), qt.Equals, filepath.FromSlash("[/a/b/{c,e} /c/d/e]")) 499 500 // Make sure the original is preserved 501 c.Assert(in, qt.DeepEquals, inCopy) 502 } 503 504 func TestExtractRootPaths(t *testing.T) { 505 tests := []struct { 506 input []string 507 expected []string 508 }{{ 509 []string{ 510 filepath.FromSlash("a/b"), filepath.FromSlash("a/b/c/"), "b", 511 filepath.FromSlash("/c/d"), filepath.FromSlash("d/"), filepath.FromSlash("//e//"), 512 }, 513 []string{"a", "a", "b", "c", "d", "e"}, 514 }} 515 516 for _, test := range tests { 517 output := ExtractRootPaths(test.input) 518 if !reflect.DeepEqual(output, test.expected) { 519 t.Errorf("Expected %#v, got %#v\n", test.expected, output) 520 } 521 } 522 } 523 524 func TestFindCWD(t *testing.T) { 525 type test struct { 526 expectedDir string 527 expectedErr error 528 } 529 530 // cwd, _ := os.Getwd() 531 data := []test{ 532 //{cwd, nil}, 533 // Commenting this out. It doesn't work properly. 534 // There's a good reason why we don't use os.Getwd(), it doesn't actually work the way we want it to. 535 // I really don't know a better way to test this function. - SPF 2014.11.04 536 } 537 for i, d := range data { 538 dir, err := FindCWD() 539 if d.expectedDir != dir { 540 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedDir, dir) 541 } 542 if d.expectedErr != err { 543 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedErr, err) 544 } 545 } 546 } 547 548 func TestSafeWriteToDisk(t *testing.T) { 549 emptyFile, _ := createZeroSizedFileInTempDir() 550 defer deleteFileInTempDir(emptyFile) 551 tmpDir, _ := createEmptyTempDir() 552 defer deleteTempDir(tmpDir) 553 554 randomString := "This is a random string!" 555 reader := strings.NewReader(randomString) 556 557 fileExists := fmt.Errorf("%v already exists", emptyFile.Name()) 558 559 type test struct { 560 filename string 561 expectedErr error 562 } 563 564 now := time.Now().Unix() 565 nowStr := strconv.FormatInt(now, 10) 566 data := []test{ 567 {emptyFile.Name(), fileExists}, 568 {tmpDir + "/" + nowStr, nil}, 569 } 570 571 for i, d := range data { 572 e := SafeWriteToDisk(d.filename, reader, new(afero.OsFs)) 573 if d.expectedErr != nil { 574 if d.expectedErr.Error() != e.Error() { 575 t.Errorf("Test %d failed. Expected error %q but got %q", i, d.expectedErr.Error(), e.Error()) 576 } 577 } else { 578 if d.expectedErr != e { 579 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedErr, e) 580 } 581 contents, _ := ioutil.ReadFile(d.filename) 582 if randomString != string(contents) { 583 t.Errorf("Test %d failed. Expected contents %q but got %q", i, randomString, string(contents)) 584 } 585 } 586 reader.Seek(0, 0) 587 } 588 } 589 590 func TestWriteToDisk(t *testing.T) { 591 emptyFile, _ := createZeroSizedFileInTempDir() 592 defer deleteFileInTempDir(emptyFile) 593 tmpDir, _ := createEmptyTempDir() 594 defer deleteTempDir(tmpDir) 595 596 randomString := "This is a random string!" 597 reader := strings.NewReader(randomString) 598 599 type test struct { 600 filename string 601 expectedErr error 602 } 603 604 now := time.Now().Unix() 605 nowStr := strconv.FormatInt(now, 10) 606 data := []test{ 607 {emptyFile.Name(), nil}, 608 {tmpDir + "/" + nowStr, nil}, 609 } 610 611 for i, d := range data { 612 e := WriteToDisk(d.filename, reader, new(afero.OsFs)) 613 if d.expectedErr != e { 614 t.Errorf("Test %d failed. WriteToDisk Error Expected %q but got %q", i, d.expectedErr, e) 615 } 616 contents, e := ioutil.ReadFile(d.filename) 617 if e != nil { 618 t.Errorf("Test %d failed. Could not read file %s. Reason: %s\n", i, d.filename, e) 619 } 620 if randomString != string(contents) { 621 t.Errorf("Test %d failed. Expected contents %q but got %q", i, randomString, string(contents)) 622 } 623 reader.Seek(0, 0) 624 } 625 } 626 627 func TestGetTempDir(t *testing.T) { 628 dir := os.TempDir() 629 if FilePathSeparator != dir[len(dir)-1:] { 630 dir = dir + FilePathSeparator 631 } 632 testDir := "hugoTestFolder" + FilePathSeparator 633 tests := []struct { 634 input string 635 expected string 636 }{ 637 {"", dir}, 638 {testDir + " Foo bar ", dir + testDir + " Foo bar " + FilePathSeparator}, 639 {testDir + "Foo.Bar/foo_Bar-Foo", dir + testDir + "Foo.Bar/foo_Bar-Foo" + FilePathSeparator}, 640 {testDir + "fOO,bar:foo%bAR", dir + testDir + "fOObarfoo%bAR" + FilePathSeparator}, 641 {testDir + "fOO,bar:foobAR", dir + testDir + "fOObarfoobAR" + FilePathSeparator}, 642 {testDir + "FOo/BaR.html", dir + testDir + "FOo/BaR.html" + FilePathSeparator}, 643 {testDir + "трям/трям", dir + testDir + "трям/трям" + FilePathSeparator}, 644 {testDir + "은행", dir + testDir + "은행" + FilePathSeparator}, 645 {testDir + "Банковский кассир", dir + testDir + "Банковский кассир" + FilePathSeparator}, 646 } 647 648 for _, test := range tests { 649 output := GetTempDir(test.input, new(afero.MemMapFs)) 650 if output != test.expected { 651 t.Errorf("Expected %#v, got %#v\n", test.expected, output) 652 } 653 } 654 }