PageRenderTime 77ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/helpers/path_test.go

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