mirror of
				https://github.com/gohugoio/hugo.git
				synced 2024-05-11 05:54:58 +00:00 
			
		
		
		
	This commit is not the smallest in Hugo's history. Some hightlights include: * Page bundles (for complete articles, keeping images and content together etc.). * Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`. * Processed images are cached inside `resources/_gen/images` (default) in your project. * Symbolic links (both files and dirs) are now allowed anywhere inside /content * A new table based build summary * The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below). A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory: ```bash ▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render" benchmark old ns/op new ns/op delta BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30% BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86% benchmark old allocs new allocs delta BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47% BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30% benchmark old bytes new bytes delta BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12% BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64% ``` Fixes #3651 Closes #3158 Fixes #1014 Closes #2021 Fixes #1240 Updates #3757
		
			
				
	
	
		
			796 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			796 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2015 The Hugo Authors. All rights reserved.
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| // http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package helpers
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"reflect"
 | |
| 	"runtime"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 
 | |
| 	"github.com/gohugoio/hugo/hugofs"
 | |
| 	"github.com/spf13/afero"
 | |
| 	"github.com/spf13/viper"
 | |
| )
 | |
| 
 | |
| func TestMakePath(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		input         string
 | |
| 		expected      string
 | |
| 		removeAccents bool
 | |
| 	}{
 | |
| 		{"  Foo bar  ", "Foo-bar", true},
 | |
| 		{"Foo.Bar/foo_Bar-Foo", "Foo.Bar/foo_Bar-Foo", true},
 | |
| 		{"fOO,bar:foobAR", "fOObarfoobAR", true},
 | |
| 		{"FOo/BaR.html", "FOo/BaR.html", true},
 | |
| 		{"трям/трям", "трям/трям", true},
 | |
| 		{"은행", "은행", true},
 | |
| 		{"Банковский кассир", "Банковскии-кассир", true},
 | |
| 		// Issue #1488
 | |
| 		{"संस्कृत", "संस्कृत", false},
 | |
| 		{"a%C3%B1ame", "a%C3%B1ame", false},         // Issue #1292
 | |
| 		{"this+is+a+test", "this+is+a+test", false}, // Issue #1290
 | |
| 		{"~foo", "~foo", false},                     // Issue #2177
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		v := viper.New()
 | |
| 		l := NewDefaultLanguage(v)
 | |
| 		v.Set("removePathAccents", test.removeAccents)
 | |
| 		p, err := NewPathSpec(hugofs.NewMem(v), l)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		output := p.MakePath(test.input)
 | |
| 		if output != test.expected {
 | |
| 			t.Errorf("Expected %#v, got %#v\n", test.expected, output)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestMakePathSanitized(t *testing.T) {
 | |
| 	v := viper.New()
 | |
| 	l := NewDefaultLanguage(v)
 | |
| 	p, _ := NewPathSpec(hugofs.NewMem(v), l)
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		input    string
 | |
| 		expected string
 | |
| 	}{
 | |
| 		{"  FOO bar  ", "foo-bar"},
 | |
| 		{"Foo.Bar/fOO_bAr-Foo", "foo.bar/foo_bar-foo"},
 | |
| 		{"FOO,bar:FooBar", "foobarfoobar"},
 | |
| 		{"foo/BAR.HTML", "foo/bar.html"},
 | |
| 		{"трям/трям", "трям/трям"},
 | |
| 		{"은행", "은행"},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		output := p.MakePathSanitized(test.input)
 | |
| 		if output != test.expected {
 | |
| 			t.Errorf("Expected %#v, got %#v\n", test.expected, output)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestMakePathSanitizedDisablePathToLower(t *testing.T) {
 | |
| 	v := viper.New()
 | |
| 
 | |
| 	v.Set("disablePathToLower", true)
 | |
| 
 | |
| 	l := NewDefaultLanguage(v)
 | |
| 	p, _ := NewPathSpec(hugofs.NewMem(v), l)
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		input    string
 | |
| 		expected string
 | |
| 	}{
 | |
| 		{"  FOO bar  ", "FOO-bar"},
 | |
| 		{"Foo.Bar/fOO_bAr-Foo", "Foo.Bar/fOO_bAr-Foo"},
 | |
| 		{"FOO,bar:FooBar", "FOObarFooBar"},
 | |
| 		{"foo/BAR.HTML", "foo/BAR.HTML"},
 | |
| 		{"трям/трям", "трям/трям"},
 | |
| 		{"은행", "은행"},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		output := p.MakePathSanitized(test.input)
 | |
| 		if output != test.expected {
 | |
| 			t.Errorf("Expected %#v, got %#v\n", test.expected, output)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetRelativePath(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		path   string
 | |
| 		base   string
 | |
| 		expect interface{}
 | |
| 	}{
 | |
| 		{filepath.FromSlash("/a/b"), filepath.FromSlash("/a"), filepath.FromSlash("b")},
 | |
| 		{filepath.FromSlash("/a/b/c/"), filepath.FromSlash("/a"), filepath.FromSlash("b/c/")},
 | |
| 		{filepath.FromSlash("/c"), filepath.FromSlash("/a/b"), filepath.FromSlash("../../c")},
 | |
| 		{filepath.FromSlash("/c"), "", false},
 | |
| 	}
 | |
| 	for i, this := range tests {
 | |
| 		// ultimately a fancy wrapper around filepath.Rel
 | |
| 		result, err := GetRelativePath(this.path, this.base)
 | |
| 
 | |
| 		if b, ok := this.expect.(bool); ok && !b {
 | |
| 			if err == nil {
 | |
| 				t.Errorf("[%d] GetRelativePath didn't return an expected error", i)
 | |
| 			}
 | |
| 		} else {
 | |
| 			if err != nil {
 | |
| 				t.Errorf("[%d] GetRelativePath failed: %s", i, err)
 | |
| 				continue
 | |
| 			}
 | |
| 			if result != this.expect {
 | |
| 				t.Errorf("[%d] GetRelativePath got %v but expected %v", i, result, this.expect)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetRealPath(t *testing.T) {
 | |
| 	if runtime.GOOS == "windows" && os.Getenv("CI") == "" {
 | |
| 		t.Skip("Skip TestGetRealPath as os.Symlink needs administrator rights on Windows")
 | |
| 	}
 | |
| 
 | |
| 	d1, err := ioutil.TempDir("", "d1")
 | |
| 	defer os.Remove(d1)
 | |
| 	fs := afero.NewOsFs()
 | |
| 
 | |
| 	rp1, err := GetRealPath(fs, d1)
 | |
| 	require.NoError(t, err)
 | |
| 	assert.Equal(t, d1, rp1)
 | |
| 
 | |
| 	sym := filepath.Join(os.TempDir(), "d1sym")
 | |
| 	err = os.Symlink(d1, sym)
 | |
| 	require.NoError(t, err)
 | |
| 	defer os.Remove(sym)
 | |
| 
 | |
| 	rp2, err := GetRealPath(fs, sym)
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	// On OS X, the temp folder is itself a symbolic link (to /private...)
 | |
| 	// This has to do for now.
 | |
| 	assert.True(t, strings.HasSuffix(rp2, d1))
 | |
| 
 | |
| }
 | |
| 
 | |
| func TestMakePathRelative(t *testing.T) {
 | |
| 	type test struct {
 | |
| 		inPath, path1, path2, output string
 | |
| 	}
 | |
| 
 | |
| 	data := []test{
 | |
| 		{"/abc/bcd/ab.css", "/abc/bcd", "/bbc/bcd", "/ab.css"},
 | |
| 		{"/abc/bcd/ab.css", "/abcd/bcd", "/abc/bcd", "/ab.css"},
 | |
| 	}
 | |
| 
 | |
| 	for i, d := range data {
 | |
| 		output, _ := makePathRelative(d.inPath, d.path1, d.path2)
 | |
| 		if d.output != output {
 | |
| 			t.Errorf("Test #%d failed. Expected %q got %q", i, d.output, output)
 | |
| 		}
 | |
| 	}
 | |
| 	_, error := makePathRelative("a/b/c.ss", "/a/c", "/d/c", "/e/f")
 | |
| 
 | |
| 	if error == nil {
 | |
| 		t.Errorf("Test failed, expected error")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetDottedRelativePath(t *testing.T) {
 | |
| 	// on Windows this will receive both kinds, both country and western ...
 | |
| 	for _, f := range []func(string) string{filepath.FromSlash, func(s string) string { return s }} {
 | |
| 		doTestGetDottedRelativePath(f, t)
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| func doTestGetDottedRelativePath(urlFixer func(string) string, t *testing.T) {
 | |
| 	type test struct {
 | |
| 		input, expected string
 | |
| 	}
 | |
| 	data := []test{
 | |
| 		{"", "./"},
 | |
| 		{urlFixer("/"), "./"},
 | |
| 		{urlFixer("post"), "../"},
 | |
| 		{urlFixer("/post"), "../"},
 | |
| 		{urlFixer("post/"), "../"},
 | |
| 		{urlFixer("tags/foo.html"), "../"},
 | |
| 		{urlFixer("/tags/foo.html"), "../"},
 | |
| 		{urlFixer("/post/"), "../"},
 | |
| 		{urlFixer("////post/////"), "../"},
 | |
| 		{urlFixer("/foo/bar/index.html"), "../../"},
 | |
| 		{urlFixer("/foo/bar/foo/"), "../../../"},
 | |
| 		{urlFixer("/foo/bar/foo"), "../../../"},
 | |
| 		{urlFixer("foo/bar/foo/"), "../../../"},
 | |
| 		{urlFixer("foo/bar/foo/bar"), "../../../../"},
 | |
| 		{"404.html", "./"},
 | |
| 		{"404.xml", "./"},
 | |
| 		{"/404.html", "./"},
 | |
| 	}
 | |
| 	for i, d := range data {
 | |
| 		output := GetDottedRelativePath(d.input)
 | |
| 		if d.expected != output {
 | |
| 			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestMakeTitle(t *testing.T) {
 | |
| 	type test struct {
 | |
| 		input, expected string
 | |
| 	}
 | |
| 	data := []test{
 | |
| 		{"Make-Title", "Make Title"},
 | |
| 		{"MakeTitle", "MakeTitle"},
 | |
| 		{"make_title", "make_title"},
 | |
| 	}
 | |
| 	for i, d := range data {
 | |
| 		output := MakeTitle(d.input)
 | |
| 		if d.expected != output {
 | |
| 			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Replace Extension is probably poorly named, but the intent of the
 | |
| // function is to accept a path and return only the file name with a
 | |
| // new extension. It's intentionally designed to strip out the path
 | |
| // and only provide the name. We should probably rename the function to
 | |
| // be more explicit at some point.
 | |
| func TestReplaceExtension(t *testing.T) {
 | |
| 	type test struct {
 | |
| 		input, newext, expected string
 | |
| 	}
 | |
| 	data := []test{
 | |
| 		// These work according to the above definition
 | |
| 		{"/some/random/path/file.xml", "html", "file.html"},
 | |
| 		{"/banana.html", "xml", "banana.xml"},
 | |
| 		{"./banana.html", "xml", "banana.xml"},
 | |
| 		{"banana/pie/index.html", "xml", "index.xml"},
 | |
| 		{"../pies/fish/index.html", "xml", "index.xml"},
 | |
| 		// but these all fail
 | |
| 		{"filename-without-an-ext", "ext", "filename-without-an-ext.ext"},
 | |
| 		{"/filename-without-an-ext", "ext", "filename-without-an-ext.ext"},
 | |
| 		{"/directory/mydir/", "ext", ".ext"},
 | |
| 		{"mydir/", "ext", ".ext"},
 | |
| 	}
 | |
| 
 | |
| 	for i, d := range data {
 | |
| 		output := ReplaceExtension(filepath.FromSlash(d.input), d.newext)
 | |
| 		if d.expected != output {
 | |
| 			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDirExists(t *testing.T) {
 | |
| 	type test struct {
 | |
| 		input    string
 | |
| 		expected bool
 | |
| 	}
 | |
| 
 | |
| 	data := []test{
 | |
| 		{".", true},
 | |
| 		{"./", true},
 | |
| 		{"..", true},
 | |
| 		{"../", true},
 | |
| 		{"./..", true},
 | |
| 		{"./../", true},
 | |
| 		{os.TempDir(), true},
 | |
| 		{os.TempDir() + FilePathSeparator, true},
 | |
| 		{"/", true},
 | |
| 		{"/some-really-random-directory-name", false},
 | |
| 		{"/some/really/random/directory/name", false},
 | |
| 		{"./some-really-random-local-directory-name", false},
 | |
| 		{"./some/really/random/local/directory/name", false},
 | |
| 	}
 | |
| 
 | |
| 	for i, d := range data {
 | |
| 		exists, _ := DirExists(filepath.FromSlash(d.input), new(afero.OsFs))
 | |
| 		if d.expected != exists {
 | |
| 			t.Errorf("Test %d failed. Expected %t got %t", i, d.expected, exists)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestIsDir(t *testing.T) {
 | |
| 	type test struct {
 | |
| 		input    string
 | |
| 		expected bool
 | |
| 	}
 | |
| 	data := []test{
 | |
| 		{"./", true},
 | |
| 		{"/", true},
 | |
| 		{"./this-directory-does-not-existi", false},
 | |
| 		{"/this-absolute-directory/does-not-exist", false},
 | |
| 	}
 | |
| 
 | |
| 	for i, d := range data {
 | |
| 
 | |
| 		exists, _ := IsDir(d.input, new(afero.OsFs))
 | |
| 		if d.expected != exists {
 | |
| 			t.Errorf("Test %d failed. Expected %t got %t", i, d.expected, exists)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestIsEmpty(t *testing.T) {
 | |
| 	zeroSizedFile, _ := createZeroSizedFileInTempDir()
 | |
| 	defer deleteFileInTempDir(zeroSizedFile)
 | |
| 	nonZeroSizedFile, _ := createNonZeroSizedFileInTempDir()
 | |
| 	defer deleteFileInTempDir(nonZeroSizedFile)
 | |
| 	emptyDirectory, _ := createEmptyTempDir()
 | |
| 	defer deleteTempDir(emptyDirectory)
 | |
| 	nonEmptyZeroLengthFilesDirectory, _ := createTempDirWithZeroLengthFiles()
 | |
| 	defer deleteTempDir(nonEmptyZeroLengthFilesDirectory)
 | |
| 	nonEmptyNonZeroLengthFilesDirectory, _ := createTempDirWithNonZeroLengthFiles()
 | |
| 	defer deleteTempDir(nonEmptyNonZeroLengthFilesDirectory)
 | |
| 	nonExistentFile := os.TempDir() + "/this-file-does-not-exist.txt"
 | |
| 	nonExistentDir := os.TempDir() + "/this/directory/does/not/exist/"
 | |
| 
 | |
| 	fileDoesNotExist := fmt.Errorf("%q path does not exist", nonExistentFile)
 | |
| 	dirDoesNotExist := fmt.Errorf("%q path does not exist", nonExistentDir)
 | |
| 
 | |
| 	type test struct {
 | |
| 		input          string
 | |
| 		expectedResult bool
 | |
| 		expectedErr    error
 | |
| 	}
 | |
| 
 | |
| 	data := []test{
 | |
| 		{zeroSizedFile.Name(), true, nil},
 | |
| 		{nonZeroSizedFile.Name(), false, nil},
 | |
| 		{emptyDirectory, true, nil},
 | |
| 		{nonEmptyZeroLengthFilesDirectory, false, nil},
 | |
| 		{nonEmptyNonZeroLengthFilesDirectory, false, nil},
 | |
| 		{nonExistentFile, false, fileDoesNotExist},
 | |
| 		{nonExistentDir, false, dirDoesNotExist},
 | |
| 	}
 | |
| 	for i, d := range data {
 | |
| 		exists, err := IsEmpty(d.input, new(afero.OsFs))
 | |
| 		if d.expectedResult != exists {
 | |
| 			t.Errorf("Test %d failed. Expected result %t got %t", i, d.expectedResult, exists)
 | |
| 		}
 | |
| 		if d.expectedErr != nil {
 | |
| 			if d.expectedErr.Error() != err.Error() {
 | |
| 				t.Errorf("Test %d failed. Expected %q(%#v) got %q(%#v)", i, d.expectedErr, d.expectedErr, err, err)
 | |
| 			}
 | |
| 		} else {
 | |
| 			if d.expectedErr != err {
 | |
| 				t.Errorf("Test %d failed. Expected %q(%#v) got %q(%#v)", i, d.expectedErr, d.expectedErr, err, err)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func createZeroSizedFileInTempDir() (*os.File, error) {
 | |
| 	filePrefix := "_path_test_"
 | |
| 	f, e := ioutil.TempFile("", filePrefix) // dir is os.TempDir()
 | |
| 	if e != nil {
 | |
| 		// if there was an error no file was created.
 | |
| 		// => no requirement to delete the file
 | |
| 		return nil, e
 | |
| 	}
 | |
| 	return f, nil
 | |
| }
 | |
| 
 | |
| func createNonZeroSizedFileInTempDir() (*os.File, error) {
 | |
| 	f, err := createZeroSizedFileInTempDir()
 | |
| 	if err != nil {
 | |
| 		// no file ??
 | |
| 	}
 | |
| 	byteString := []byte("byteString")
 | |
| 	err = ioutil.WriteFile(f.Name(), byteString, 0644)
 | |
| 	if err != nil {
 | |
| 		// delete the file
 | |
| 		deleteFileInTempDir(f)
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return f, nil
 | |
| }
 | |
| 
 | |
| func deleteFileInTempDir(f *os.File) {
 | |
| 	err := os.Remove(f.Name())
 | |
| 	if err != nil {
 | |
| 		// now what?
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func createEmptyTempDir() (string, error) {
 | |
| 	dirPrefix := "_dir_prefix_"
 | |
| 	d, e := ioutil.TempDir("", dirPrefix) // will be in os.TempDir()
 | |
| 	if e != nil {
 | |
| 		// no directory to delete - it was never created
 | |
| 		return "", e
 | |
| 	}
 | |
| 	return d, nil
 | |
| }
 | |
| 
 | |
| func createTempDirWithZeroLengthFiles() (string, error) {
 | |
| 	d, dirErr := createEmptyTempDir()
 | |
| 	if dirErr != nil {
 | |
| 		//now what?
 | |
| 	}
 | |
| 	filePrefix := "_path_test_"
 | |
| 	_, fileErr := ioutil.TempFile(d, filePrefix) // dir is os.TempDir()
 | |
| 	if fileErr != nil {
 | |
| 		// if there was an error no file was created.
 | |
| 		// but we need to remove the directory to clean-up
 | |
| 		deleteTempDir(d)
 | |
| 		return "", fileErr
 | |
| 	}
 | |
| 	// the dir now has one, zero length file in it
 | |
| 	return d, nil
 | |
| 
 | |
| }
 | |
| 
 | |
| func createTempDirWithNonZeroLengthFiles() (string, error) {
 | |
| 	d, dirErr := createEmptyTempDir()
 | |
| 	if dirErr != nil {
 | |
| 		//now what?
 | |
| 	}
 | |
| 	filePrefix := "_path_test_"
 | |
| 	f, fileErr := ioutil.TempFile(d, filePrefix) // dir is os.TempDir()
 | |
| 	if fileErr != nil {
 | |
| 		// if there was an error no file was created.
 | |
| 		// but we need to remove the directory to clean-up
 | |
| 		deleteTempDir(d)
 | |
| 		return "", fileErr
 | |
| 	}
 | |
| 	byteString := []byte("byteString")
 | |
| 	fileErr = ioutil.WriteFile(f.Name(), byteString, 0644)
 | |
| 	if fileErr != nil {
 | |
| 		// delete the file
 | |
| 		deleteFileInTempDir(f)
 | |
| 		// also delete the directory
 | |
| 		deleteTempDir(d)
 | |
| 		return "", fileErr
 | |
| 	}
 | |
| 
 | |
| 	// the dir now has one, zero length file in it
 | |
| 	return d, nil
 | |
| 
 | |
| }
 | |
| 
 | |
| func deleteTempDir(d string) {
 | |
| 	err := os.RemoveAll(d)
 | |
| 	if err != nil {
 | |
| 		// now what?
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestExists(t *testing.T) {
 | |
| 	zeroSizedFile, _ := createZeroSizedFileInTempDir()
 | |
| 	defer deleteFileInTempDir(zeroSizedFile)
 | |
| 	nonZeroSizedFile, _ := createNonZeroSizedFileInTempDir()
 | |
| 	defer deleteFileInTempDir(nonZeroSizedFile)
 | |
| 	emptyDirectory, _ := createEmptyTempDir()
 | |
| 	defer deleteTempDir(emptyDirectory)
 | |
| 	nonExistentFile := os.TempDir() + "/this-file-does-not-exist.txt"
 | |
| 	nonExistentDir := os.TempDir() + "/this/directory/does/not/exist/"
 | |
| 
 | |
| 	type test struct {
 | |
| 		input          string
 | |
| 		expectedResult bool
 | |
| 		expectedErr    error
 | |
| 	}
 | |
| 
 | |
| 	data := []test{
 | |
| 		{zeroSizedFile.Name(), true, nil},
 | |
| 		{nonZeroSizedFile.Name(), true, nil},
 | |
| 		{emptyDirectory, true, nil},
 | |
| 		{nonExistentFile, false, nil},
 | |
| 		{nonExistentDir, false, nil},
 | |
| 	}
 | |
| 	for i, d := range data {
 | |
| 		exists, err := Exists(d.input, new(afero.OsFs))
 | |
| 		if d.expectedResult != exists {
 | |
| 			t.Errorf("Test %d failed. Expected result %t got %t", i, d.expectedResult, exists)
 | |
| 		}
 | |
| 		if d.expectedErr != err {
 | |
| 			t.Errorf("Test %d failed. Expected %q got %q", i, d.expectedErr, err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| func TestAbsPathify(t *testing.T) {
 | |
| 	defer viper.Reset()
 | |
| 
 | |
| 	type test struct {
 | |
| 		inPath, workingDir, expected string
 | |
| 	}
 | |
| 	data := []test{
 | |
| 		{os.TempDir(), filepath.FromSlash("/work"), filepath.Clean(os.TempDir())}, // TempDir has trailing slash
 | |
| 		{"dir", filepath.FromSlash("/work"), filepath.FromSlash("/work/dir")},
 | |
| 	}
 | |
| 
 | |
| 	windowsData := []test{
 | |
| 		{"c:\\banana\\..\\dir", "c:\\foo", "c:\\dir"},
 | |
| 		{"\\dir", "c:\\foo", "c:\\foo\\dir"},
 | |
| 		{"c:\\", "c:\\foo", "c:\\"},
 | |
| 	}
 | |
| 
 | |
| 	unixData := []test{
 | |
| 		{"/banana/../dir/", "/work", "/dir"},
 | |
| 	}
 | |
| 
 | |
| 	for i, d := range data {
 | |
| 		viper.Reset()
 | |
| 		// todo see comment in AbsPathify
 | |
| 		ps := newTestDefaultPathSpec("workingDir", d.workingDir)
 | |
| 
 | |
| 		expected := ps.AbsPathify(d.inPath)
 | |
| 		if d.expected != expected {
 | |
| 			t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
 | |
| 		}
 | |
| 	}
 | |
| 	t.Logf("Running platform specific path tests for %s", runtime.GOOS)
 | |
| 	if runtime.GOOS == "windows" {
 | |
| 		for i, d := range windowsData {
 | |
| 			ps := newTestDefaultPathSpec("workingDir", d.workingDir)
 | |
| 
 | |
| 			expected := ps.AbsPathify(d.inPath)
 | |
| 			if d.expected != expected {
 | |
| 				t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		for i, d := range unixData {
 | |
| 			ps := newTestDefaultPathSpec("workingDir", d.workingDir)
 | |
| 
 | |
| 			expected := ps.AbsPathify(d.inPath)
 | |
| 			if d.expected != expected {
 | |
| 				t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| func TestFilename(t *testing.T) {
 | |
| 	type test struct {
 | |
| 		input, expected string
 | |
| 	}
 | |
| 	data := []test{
 | |
| 		{"index.html", "index"},
 | |
| 		{"./index.html", "index"},
 | |
| 		{"/index.html", "index"},
 | |
| 		{"index", "index"},
 | |
| 		{"/tmp/index.html", "index"},
 | |
| 		{"./filename-no-ext", "filename-no-ext"},
 | |
| 		{"/filename-no-ext", "filename-no-ext"},
 | |
| 		{"filename-no-ext", "filename-no-ext"},
 | |
| 		{"directory/", ""}, // no filename case??
 | |
| 		{"directory/.hidden.ext", ".hidden"},
 | |
| 		{"./directory/../~/banana/gold.fish", "gold"},
 | |
| 		{"../directory/banana.man", "banana"},
 | |
| 		{"~/mydir/filename.ext", "filename"},
 | |
| 		{"./directory//tmp/filename.ext", "filename"},
 | |
| 	}
 | |
| 
 | |
| 	for i, d := range data {
 | |
| 		output := Filename(filepath.FromSlash(d.input))
 | |
| 		if d.expected != output {
 | |
| 			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestFileAndExt(t *testing.T) {
 | |
| 	type test struct {
 | |
| 		input, expectedFile, expectedExt string
 | |
| 	}
 | |
| 	data := []test{
 | |
| 		{"index.html", "index", ".html"},
 | |
| 		{"./index.html", "index", ".html"},
 | |
| 		{"/index.html", "index", ".html"},
 | |
| 		{"index", "index", ""},
 | |
| 		{"/tmp/index.html", "index", ".html"},
 | |
| 		{"./filename-no-ext", "filename-no-ext", ""},
 | |
| 		{"/filename-no-ext", "filename-no-ext", ""},
 | |
| 		{"filename-no-ext", "filename-no-ext", ""},
 | |
| 		{"directory/", "", ""}, // no filename case??
 | |
| 		{"directory/.hidden.ext", ".hidden", ".ext"},
 | |
| 		{"./directory/../~/banana/gold.fish", "gold", ".fish"},
 | |
| 		{"../directory/banana.man", "banana", ".man"},
 | |
| 		{"~/mydir/filename.ext", "filename", ".ext"},
 | |
| 		{"./directory//tmp/filename.ext", "filename", ".ext"},
 | |
| 	}
 | |
| 
 | |
| 	for i, d := range data {
 | |
| 		file, ext := fileAndExt(filepath.FromSlash(d.input), fpb)
 | |
| 		if d.expectedFile != file {
 | |
| 			t.Errorf("Test %d failed. Expected filename %q got %q.", i, d.expectedFile, file)
 | |
| 		}
 | |
| 		if d.expectedExt != ext {
 | |
| 			t.Errorf("Test %d failed. Expected extension %q got %q.", i, d.expectedExt, ext)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| func TestPathPrep(t *testing.T) {
 | |
| 
 | |
| }
 | |
| 
 | |
| func TestPrettifyPath(t *testing.T) {
 | |
| 
 | |
| }
 | |
| 
 | |
| func TestExtractRootPaths(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		input    []string
 | |
| 		expected []string
 | |
| 	}{{[]string{filepath.FromSlash("a/b"), filepath.FromSlash("a/b/c/"), "b",
 | |
| 		filepath.FromSlash("/c/d"), filepath.FromSlash("d/"), filepath.FromSlash("//e//")},
 | |
| 		[]string{"a", "a", "b", "c", "d", "e"}}}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		output := ExtractRootPaths(test.input)
 | |
| 		if !reflect.DeepEqual(output, test.expected) {
 | |
| 			t.Errorf("Expected %#v, got %#v\n", test.expected, output)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestFindCWD(t *testing.T) {
 | |
| 	type test struct {
 | |
| 		expectedDir string
 | |
| 		expectedErr error
 | |
| 	}
 | |
| 
 | |
| 	//cwd, _ := os.Getwd()
 | |
| 	data := []test{
 | |
| 	//{cwd, nil},
 | |
| 	// Commenting this out. It doesn't work properly.
 | |
| 	// There's a good reason why we don't use os.Getwd(), it doesn't actually work the way we want it to.
 | |
| 	// I really don't know a better way to test this function. - SPF 2014.11.04
 | |
| 	}
 | |
| 	for i, d := range data {
 | |
| 		dir, err := FindCWD()
 | |
| 		if d.expectedDir != dir {
 | |
| 			t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedDir, dir)
 | |
| 		}
 | |
| 		if d.expectedErr != err {
 | |
| 			t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedErr, err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSafeWriteToDisk(t *testing.T) {
 | |
| 	emptyFile, _ := createZeroSizedFileInTempDir()
 | |
| 	defer deleteFileInTempDir(emptyFile)
 | |
| 	tmpDir, _ := createEmptyTempDir()
 | |
| 	defer deleteTempDir(tmpDir)
 | |
| 
 | |
| 	randomString := "This is a random string!"
 | |
| 	reader := strings.NewReader(randomString)
 | |
| 
 | |
| 	fileExists := fmt.Errorf("%v already exists", emptyFile.Name())
 | |
| 
 | |
| 	type test struct {
 | |
| 		filename    string
 | |
| 		expectedErr error
 | |
| 	}
 | |
| 
 | |
| 	now := time.Now().Unix()
 | |
| 	nowStr := strconv.FormatInt(now, 10)
 | |
| 	data := []test{
 | |
| 		{emptyFile.Name(), fileExists},
 | |
| 		{tmpDir + "/" + nowStr, nil},
 | |
| 	}
 | |
| 
 | |
| 	for i, d := range data {
 | |
| 		e := SafeWriteToDisk(d.filename, reader, new(afero.OsFs))
 | |
| 		if d.expectedErr != nil {
 | |
| 			if d.expectedErr.Error() != e.Error() {
 | |
| 				t.Errorf("Test %d failed. Expected error %q but got %q", i, d.expectedErr.Error(), e.Error())
 | |
| 			}
 | |
| 		} else {
 | |
| 			if d.expectedErr != e {
 | |
| 				t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedErr, e)
 | |
| 			}
 | |
| 			contents, _ := ioutil.ReadFile(d.filename)
 | |
| 			if randomString != string(contents) {
 | |
| 				t.Errorf("Test %d failed. Expected contents %q but got %q", i, randomString, string(contents))
 | |
| 			}
 | |
| 		}
 | |
| 		reader.Seek(0, 0)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestWriteToDisk(t *testing.T) {
 | |
| 	emptyFile, _ := createZeroSizedFileInTempDir()
 | |
| 	defer deleteFileInTempDir(emptyFile)
 | |
| 	tmpDir, _ := createEmptyTempDir()
 | |
| 	defer deleteTempDir(tmpDir)
 | |
| 
 | |
| 	randomString := "This is a random string!"
 | |
| 	reader := strings.NewReader(randomString)
 | |
| 
 | |
| 	type test struct {
 | |
| 		filename    string
 | |
| 		expectedErr error
 | |
| 	}
 | |
| 
 | |
| 	now := time.Now().Unix()
 | |
| 	nowStr := strconv.FormatInt(now, 10)
 | |
| 	data := []test{
 | |
| 		{emptyFile.Name(), nil},
 | |
| 		{tmpDir + "/" + nowStr, nil},
 | |
| 	}
 | |
| 
 | |
| 	for i, d := range data {
 | |
| 		e := WriteToDisk(d.filename, reader, new(afero.OsFs))
 | |
| 		if d.expectedErr != e {
 | |
| 			t.Errorf("Test %d failed. WriteToDisk Error Expected %q but got %q", i, d.expectedErr, e)
 | |
| 		}
 | |
| 		contents, e := ioutil.ReadFile(d.filename)
 | |
| 		if e != nil {
 | |
| 			t.Errorf("Test %d failed. Could not read file %s. Reason: %s\n", i, d.filename, e)
 | |
| 		}
 | |
| 		if randomString != string(contents) {
 | |
| 			t.Errorf("Test %d failed. Expected contents %q but got %q", i, randomString, string(contents))
 | |
| 		}
 | |
| 		reader.Seek(0, 0)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetTempDir(t *testing.T) {
 | |
| 	dir := os.TempDir()
 | |
| 	if FilePathSeparator != dir[len(dir)-1:] {
 | |
| 		dir = dir + FilePathSeparator
 | |
| 	}
 | |
| 	testDir := "hugoTestFolder" + FilePathSeparator
 | |
| 	tests := []struct {
 | |
| 		input    string
 | |
| 		expected string
 | |
| 	}{
 | |
| 		{"", dir},
 | |
| 		{testDir + "  Foo bar  ", dir + testDir + "  Foo bar  " + FilePathSeparator},
 | |
| 		{testDir + "Foo.Bar/foo_Bar-Foo", dir + testDir + "Foo.Bar/foo_Bar-Foo" + FilePathSeparator},
 | |
| 		{testDir + "fOO,bar:foo%bAR", dir + testDir + "fOObarfoo%bAR" + FilePathSeparator},
 | |
| 		{testDir + "fOO,bar:foobAR", dir + testDir + "fOObarfoobAR" + FilePathSeparator},
 | |
| 		{testDir + "FOo/BaR.html", dir + testDir + "FOo/BaR.html" + FilePathSeparator},
 | |
| 		{testDir + "трям/трям", dir + testDir + "трям/трям" + FilePathSeparator},
 | |
| 		{testDir + "은행", dir + testDir + "은행" + FilePathSeparator},
 | |
| 		{testDir + "Банковский кассир", dir + testDir + "Банковский кассир" + FilePathSeparator},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		output := GetTempDir(test.input, new(afero.MemMapFs))
 | |
| 		if output != test.expected {
 | |
| 			t.Errorf("Expected %#v, got %#v\n", test.expected, output)
 | |
| 		}
 | |
| 	}
 | |
| }
 |