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 }