path_test.go (15260B)
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 createZeroSizedFileInTempDir() (*os.File, error) { 260 filePrefix := "_path_test_" 261 f, e := ioutil.TempFile("", filePrefix) // dir is os.TempDir() 262 if e != nil { 263 // if there was an error no file was created. 264 // => no requirement to delete the file 265 return nil, e 266 } 267 return f, nil 268 } 269 270 func createNonZeroSizedFileInTempDir() (*os.File, error) { 271 f, err := createZeroSizedFileInTempDir() 272 if err != nil { 273 // no file ?? 274 return nil, err 275 } 276 byteString := []byte("byteString") 277 err = ioutil.WriteFile(f.Name(), byteString, 0644) 278 if err != nil { 279 // delete the file 280 deleteFileInTempDir(f) 281 return nil, err 282 } 283 return f, nil 284 } 285 286 func deleteFileInTempDir(f *os.File) { 287 _ = os.Remove(f.Name()) 288 } 289 290 func createEmptyTempDir() (string, error) { 291 dirPrefix := "_dir_prefix_" 292 d, e := ioutil.TempDir("", dirPrefix) // will be in os.TempDir() 293 if e != nil { 294 // no directory to delete - it was never created 295 return "", e 296 } 297 return d, nil 298 } 299 300 func deleteTempDir(d string) { 301 _ = os.RemoveAll(d) 302 } 303 304 func TestExists(t *testing.T) { 305 zeroSizedFile, _ := createZeroSizedFileInTempDir() 306 defer deleteFileInTempDir(zeroSizedFile) 307 nonZeroSizedFile, _ := createNonZeroSizedFileInTempDir() 308 defer deleteFileInTempDir(nonZeroSizedFile) 309 emptyDirectory, _ := createEmptyTempDir() 310 defer deleteTempDir(emptyDirectory) 311 nonExistentFile := os.TempDir() + "/this-file-does-not-exist.txt" 312 nonExistentDir := os.TempDir() + "/this/directory/does/not/exist/" 313 314 type test struct { 315 input string 316 expectedResult bool 317 expectedErr error 318 } 319 320 data := []test{ 321 {zeroSizedFile.Name(), true, nil}, 322 {nonZeroSizedFile.Name(), true, nil}, 323 {emptyDirectory, true, nil}, 324 {nonExistentFile, false, nil}, 325 {nonExistentDir, false, nil}, 326 } 327 for i, d := range data { 328 exists, err := Exists(d.input, new(afero.OsFs)) 329 if d.expectedResult != exists { 330 t.Errorf("Test %d failed. Expected result %t got %t", i, d.expectedResult, exists) 331 } 332 if d.expectedErr != err { 333 t.Errorf("Test %d failed. Expected %q got %q", i, d.expectedErr, err) 334 } 335 } 336 } 337 338 func TestAbsPathify(t *testing.T) { 339 type test struct { 340 inPath, workingDir, expected string 341 } 342 data := []test{ 343 {os.TempDir(), filepath.FromSlash("/work"), filepath.Clean(os.TempDir())}, // TempDir has trailing slash 344 {"dir", filepath.FromSlash("/work"), filepath.FromSlash("/work/dir")}, 345 } 346 347 windowsData := []test{ 348 {"c:\\banana\\..\\dir", "c:\\foo", "c:\\dir"}, 349 {"\\dir", "c:\\foo", "c:\\foo\\dir"}, 350 {"c:\\", "c:\\foo", "c:\\"}, 351 } 352 353 unixData := []test{ 354 {"/banana/../dir/", "/work", "/dir"}, 355 } 356 357 for i, d := range data { 358 // todo see comment in AbsPathify 359 ps := newTestDefaultPathSpec("workingDir", d.workingDir) 360 361 expected := ps.AbsPathify(d.inPath) 362 if d.expected != expected { 363 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected) 364 } 365 } 366 t.Logf("Running platform specific path tests for %s", runtime.GOOS) 367 if runtime.GOOS == "windows" { 368 for i, d := range windowsData { 369 ps := newTestDefaultPathSpec("workingDir", d.workingDir) 370 371 expected := ps.AbsPathify(d.inPath) 372 if d.expected != expected { 373 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected) 374 } 375 } 376 } else { 377 for i, d := range unixData { 378 ps := newTestDefaultPathSpec("workingDir", d.workingDir) 379 380 expected := ps.AbsPathify(d.inPath) 381 if d.expected != expected { 382 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected) 383 } 384 } 385 } 386 } 387 388 func TestExtractAndGroupRootPaths(t *testing.T) { 389 in := []string{ 390 filepath.FromSlash("/a/b/c/d"), 391 filepath.FromSlash("/a/b/c/e"), 392 filepath.FromSlash("/a/b/e/f"), 393 filepath.FromSlash("/a/b"), 394 filepath.FromSlash("/a/b/c/b/g"), 395 filepath.FromSlash("/c/d/e"), 396 } 397 398 inCopy := make([]string, len(in)) 399 copy(inCopy, in) 400 401 result := ExtractAndGroupRootPaths(in) 402 403 c := qt.New(t) 404 c.Assert(fmt.Sprint(result), qt.Equals, filepath.FromSlash("[/a/b/{c,e} /c/d/e]")) 405 406 // Make sure the original is preserved 407 c.Assert(in, qt.DeepEquals, inCopy) 408 } 409 410 func TestExtractRootPaths(t *testing.T) { 411 tests := []struct { 412 input []string 413 expected []string 414 }{{ 415 []string{ 416 filepath.FromSlash("a/b"), filepath.FromSlash("a/b/c/"), "b", 417 filepath.FromSlash("/c/d"), filepath.FromSlash("d/"), filepath.FromSlash("//e//"), 418 }, 419 []string{"a", "a", "b", "c", "d", "e"}, 420 }} 421 422 for _, test := range tests { 423 output := ExtractRootPaths(test.input) 424 if !reflect.DeepEqual(output, test.expected) { 425 t.Errorf("Expected %#v, got %#v\n", test.expected, output) 426 } 427 } 428 } 429 430 func TestFindCWD(t *testing.T) { 431 type test struct { 432 expectedDir string 433 expectedErr error 434 } 435 436 // cwd, _ := os.Getwd() 437 data := []test{ 438 //{cwd, nil}, 439 // Commenting this out. It doesn't work properly. 440 // There's a good reason why we don't use os.Getwd(), it doesn't actually work the way we want it to. 441 // I really don't know a better way to test this function. - SPF 2014.11.04 442 } 443 for i, d := range data { 444 dir, err := FindCWD() 445 if d.expectedDir != dir { 446 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedDir, dir) 447 } 448 if d.expectedErr != err { 449 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedErr, err) 450 } 451 } 452 } 453 454 func TestSafeWriteToDisk(t *testing.T) { 455 emptyFile, _ := createZeroSizedFileInTempDir() 456 defer deleteFileInTempDir(emptyFile) 457 tmpDir, _ := createEmptyTempDir() 458 defer deleteTempDir(tmpDir) 459 460 randomString := "This is a random string!" 461 reader := strings.NewReader(randomString) 462 463 fileExists := fmt.Errorf("%v already exists", emptyFile.Name()) 464 465 type test struct { 466 filename string 467 expectedErr error 468 } 469 470 now := time.Now().Unix() 471 nowStr := strconv.FormatInt(now, 10) 472 data := []test{ 473 {emptyFile.Name(), fileExists}, 474 {tmpDir + "/" + nowStr, nil}, 475 } 476 477 for i, d := range data { 478 e := SafeWriteToDisk(d.filename, reader, new(afero.OsFs)) 479 if d.expectedErr != nil { 480 if d.expectedErr.Error() != e.Error() { 481 t.Errorf("Test %d failed. Expected error %q but got %q", i, d.expectedErr.Error(), e.Error()) 482 } 483 } else { 484 if d.expectedErr != e { 485 t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedErr, e) 486 } 487 contents, _ := ioutil.ReadFile(d.filename) 488 if randomString != string(contents) { 489 t.Errorf("Test %d failed. Expected contents %q but got %q", i, randomString, string(contents)) 490 } 491 } 492 reader.Seek(0, 0) 493 } 494 } 495 496 func TestWriteToDisk(t *testing.T) { 497 emptyFile, _ := createZeroSizedFileInTempDir() 498 defer deleteFileInTempDir(emptyFile) 499 tmpDir, _ := createEmptyTempDir() 500 defer deleteTempDir(tmpDir) 501 502 randomString := "This is a random string!" 503 reader := strings.NewReader(randomString) 504 505 type test struct { 506 filename string 507 expectedErr error 508 } 509 510 now := time.Now().Unix() 511 nowStr := strconv.FormatInt(now, 10) 512 data := []test{ 513 {emptyFile.Name(), nil}, 514 {tmpDir + "/" + nowStr, nil}, 515 } 516 517 for i, d := range data { 518 e := WriteToDisk(d.filename, reader, new(afero.OsFs)) 519 if d.expectedErr != e { 520 t.Errorf("Test %d failed. WriteToDisk Error Expected %q but got %q", i, d.expectedErr, e) 521 } 522 contents, e := ioutil.ReadFile(d.filename) 523 if e != nil { 524 t.Errorf("Test %d failed. Could not read file %s. Reason: %s\n", i, d.filename, e) 525 } 526 if randomString != string(contents) { 527 t.Errorf("Test %d failed. Expected contents %q but got %q", i, randomString, string(contents)) 528 } 529 reader.Seek(0, 0) 530 } 531 } 532 533 func TestGetTempDir(t *testing.T) { 534 dir := os.TempDir() 535 if FilePathSeparator != dir[len(dir)-1:] { 536 dir = dir + FilePathSeparator 537 } 538 testDir := "hugoTestFolder" + FilePathSeparator 539 tests := []struct { 540 input string 541 expected string 542 }{ 543 {"", dir}, 544 {testDir + " Foo bar ", dir + testDir + " Foo bar " + FilePathSeparator}, 545 {testDir + "Foo.Bar/foo_Bar-Foo", dir + testDir + "Foo.Bar/foo_Bar-Foo" + FilePathSeparator}, 546 {testDir + "fOO,bar:foo%bAR", dir + testDir + "fOObarfoo%bAR" + FilePathSeparator}, 547 {testDir + "fOO,bar:foobAR", dir + testDir + "fOObarfoobAR" + FilePathSeparator}, 548 {testDir + "FOo/BaR.html", dir + testDir + "FOo/BaR.html" + FilePathSeparator}, 549 {testDir + "трям/трям", dir + testDir + "трям/трям" + FilePathSeparator}, 550 {testDir + "은행", dir + testDir + "은행" + FilePathSeparator}, 551 {testDir + "Банковский кассир", dir + testDir + "Банковский кассир" + FilePathSeparator}, 552 } 553 554 for _, test := range tests { 555 output := GetTempDir(test.input, new(afero.MemMapFs)) 556 if output != test.expected { 557 t.Errorf("Expected %#v, got %#v\n", test.expected, output) 558 } 559 } 560 }