hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

walk_test.go (6589B)

    1 // Copyright 2019 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 hugofs
   15 
   16 import (
   17 	"context"
   18 	"fmt"
   19 	"os"
   20 	"path/filepath"
   21 	"runtime"
   22 	"strings"
   23 	"testing"
   24 
   25 	"errors"
   26 
   27 	"github.com/gohugoio/hugo/common/para"
   28 	"github.com/gohugoio/hugo/htesting"
   29 
   30 	"github.com/spf13/afero"
   31 
   32 	qt "github.com/frankban/quicktest"
   33 )
   34 
   35 func TestWalk(t *testing.T) {
   36 	c := qt.New(t)
   37 
   38 	fs := NewBaseFileDecorator(afero.NewMemMapFs())
   39 
   40 	afero.WriteFile(fs, "b.txt", []byte("content"), 0777)
   41 	afero.WriteFile(fs, "c.txt", []byte("content"), 0777)
   42 	afero.WriteFile(fs, "a.txt", []byte("content"), 0777)
   43 
   44 	names, err := collectFilenames(fs, "", "")
   45 
   46 	c.Assert(err, qt.IsNil)
   47 	c.Assert(names, qt.DeepEquals, []string{"a.txt", "b.txt", "c.txt"})
   48 }
   49 
   50 func TestWalkRootMappingFs(t *testing.T) {
   51 	c := qt.New(t)
   52 
   53 	prepare := func(c *qt.C) afero.Fs {
   54 		fs := NewBaseFileDecorator(afero.NewMemMapFs())
   55 
   56 		testfile := "test.txt"
   57 
   58 		c.Assert(afero.WriteFile(fs, filepath.Join("a/b", testfile), []byte("some content"), 0755), qt.IsNil)
   59 		c.Assert(afero.WriteFile(fs, filepath.Join("c/d", testfile), []byte("some content"), 0755), qt.IsNil)
   60 		c.Assert(afero.WriteFile(fs, filepath.Join("e/f", testfile), []byte("some content"), 0755), qt.IsNil)
   61 
   62 		rm := []RootMapping{
   63 			{
   64 				From: "static/b",
   65 				To:   "e/f",
   66 			},
   67 			{
   68 				From: "static/a",
   69 				To:   "c/d",
   70 			},
   71 
   72 			{
   73 				From: "static/c",
   74 				To:   "a/b",
   75 			},
   76 		}
   77 
   78 		rfs, err := NewRootMappingFs(fs, rm...)
   79 		c.Assert(err, qt.IsNil)
   80 		return afero.NewBasePathFs(rfs, "static")
   81 	}
   82 
   83 	c.Run("Basic", func(c *qt.C) {
   84 		bfs := prepare(c)
   85 
   86 		names, err := collectFilenames(bfs, "", "")
   87 
   88 		c.Assert(err, qt.IsNil)
   89 		c.Assert(names, qt.DeepEquals, []string{"a/test.txt", "b/test.txt", "c/test.txt"})
   90 	})
   91 
   92 	c.Run("Para", func(c *qt.C) {
   93 		bfs := prepare(c)
   94 
   95 		p := para.New(4)
   96 		r, _ := p.Start(context.Background())
   97 
   98 		for i := 0; i < 8; i++ {
   99 			r.Run(func() error {
  100 				_, err := collectFilenames(bfs, "", "")
  101 				if err != nil {
  102 					return err
  103 				}
  104 				fi, err := bfs.Stat("b/test.txt")
  105 				if err != nil {
  106 					return err
  107 				}
  108 				meta := fi.(FileMetaInfo).Meta()
  109 				if meta.Filename == "" {
  110 					return errors.New("fail")
  111 				}
  112 				return nil
  113 			})
  114 		}
  115 
  116 		c.Assert(r.Wait(), qt.IsNil)
  117 	})
  118 }
  119 
  120 func skipSymlink() bool {
  121 	if runtime.GOOS != "windows" {
  122 		return false
  123 	}
  124 	if os.Getenv("GITHUB_ACTION") != "" {
  125 		// TODO(bep) figure out why this fails on GitHub Actions.
  126 		return true
  127 	}
  128 	return os.Getenv("CI") == ""
  129 }
  130 
  131 func TestWalkSymbolicLink(t *testing.T) {
  132 	if skipSymlink() {
  133 		t.Skip("Skip; os.Symlink needs administrator rights on Windows")
  134 	}
  135 	c := qt.New(t)
  136 	workDir, clean, err := htesting.CreateTempDir(Os, "hugo-walk-sym")
  137 	c.Assert(err, qt.IsNil)
  138 	defer clean()
  139 	wd, _ := os.Getwd()
  140 	defer func() {
  141 		os.Chdir(wd)
  142 	}()
  143 
  144 	fs := NewBaseFileDecorator(Os)
  145 
  146 	blogDir := filepath.Join(workDir, "blog")
  147 	docsDir := filepath.Join(workDir, "docs")
  148 	blogReal := filepath.Join(blogDir, "real")
  149 	blogRealSub := filepath.Join(blogReal, "sub")
  150 	c.Assert(os.MkdirAll(blogRealSub, 0777), qt.IsNil)
  151 	c.Assert(os.MkdirAll(docsDir, 0777), qt.IsNil)
  152 	afero.WriteFile(fs, filepath.Join(blogRealSub, "a.txt"), []byte("content"), 0777)
  153 	afero.WriteFile(fs, filepath.Join(docsDir, "b.txt"), []byte("content"), 0777)
  154 
  155 	os.Chdir(blogDir)
  156 	c.Assert(os.Symlink("real", "symlinked"), qt.IsNil)
  157 	os.Chdir(blogReal)
  158 	c.Assert(os.Symlink("../real", "cyclic"), qt.IsNil)
  159 	os.Chdir(docsDir)
  160 	c.Assert(os.Symlink("../blog/real/cyclic", "docsreal"), qt.IsNil)
  161 
  162 	t.Run("OS Fs", func(t *testing.T) {
  163 		c := qt.New(t)
  164 
  165 		names, err := collectFilenames(fs, workDir, workDir)
  166 		c.Assert(err, qt.IsNil)
  167 
  168 		c.Assert(names, qt.DeepEquals, []string{"blog/real/sub/a.txt", "blog/symlinked/sub/a.txt", "docs/b.txt"})
  169 	})
  170 
  171 	t.Run("BasePath Fs", func(t *testing.T) {
  172 		c := qt.New(t)
  173 
  174 		docsFs := afero.NewBasePathFs(fs, docsDir)
  175 
  176 		names, err := collectFilenames(docsFs, "", "")
  177 		c.Assert(err, qt.IsNil)
  178 
  179 		// Note: the docsreal folder is considered cyclic when walking from the root, but this works.
  180 		c.Assert(names, qt.DeepEquals, []string{"b.txt", "docsreal/sub/a.txt"})
  181 	})
  182 }
  183 
  184 func collectFilenames(fs afero.Fs, base, root string) ([]string, error) {
  185 	var names []string
  186 
  187 	walkFn := func(path string, info FileMetaInfo, err error) error {
  188 		if err != nil {
  189 			return err
  190 		}
  191 
  192 		if info.IsDir() {
  193 			return nil
  194 		}
  195 
  196 		filename := info.Meta().Path
  197 		filename = filepath.ToSlash(filename)
  198 
  199 		names = append(names, filename)
  200 
  201 		return nil
  202 	}
  203 
  204 	w := NewWalkway(WalkwayConfig{Fs: fs, BasePath: base, Root: root, WalkFn: walkFn})
  205 
  206 	err := w.Walk()
  207 
  208 	return names, err
  209 }
  210 
  211 func collectFileinfos(fs afero.Fs, base, root string) ([]FileMetaInfo, error) {
  212 	var fis []FileMetaInfo
  213 
  214 	walkFn := func(path string, info FileMetaInfo, err error) error {
  215 		if err != nil {
  216 			return err
  217 		}
  218 
  219 		fis = append(fis, info)
  220 
  221 		return nil
  222 	}
  223 
  224 	w := NewWalkway(WalkwayConfig{Fs: fs, BasePath: base, Root: root, WalkFn: walkFn})
  225 
  226 	err := w.Walk()
  227 
  228 	return fis, err
  229 }
  230 
  231 func BenchmarkWalk(b *testing.B) {
  232 	c := qt.New(b)
  233 	fs := NewBaseFileDecorator(afero.NewMemMapFs())
  234 
  235 	writeFiles := func(dir string, numfiles int) {
  236 		for i := 0; i < numfiles; i++ {
  237 			filename := filepath.Join(dir, fmt.Sprintf("file%d.txt", i))
  238 			c.Assert(afero.WriteFile(fs, filename, []byte("content"), 0777), qt.IsNil)
  239 		}
  240 	}
  241 
  242 	const numFilesPerDir = 20
  243 
  244 	writeFiles("root", numFilesPerDir)
  245 	writeFiles("root/l1_1", numFilesPerDir)
  246 	writeFiles("root/l1_1/l2_1", numFilesPerDir)
  247 	writeFiles("root/l1_1/l2_2", numFilesPerDir)
  248 	writeFiles("root/l1_2", numFilesPerDir)
  249 	writeFiles("root/l1_2/l2_1", numFilesPerDir)
  250 	writeFiles("root/l1_3", numFilesPerDir)
  251 
  252 	walkFn := func(path string, info FileMetaInfo, err error) error {
  253 		if err != nil {
  254 			return err
  255 		}
  256 		if info.IsDir() {
  257 			return nil
  258 		}
  259 
  260 		filename := info.Meta().Filename
  261 		if !strings.HasPrefix(filename, "root") {
  262 			return errors.New(filename)
  263 		}
  264 
  265 		return nil
  266 	}
  267 
  268 	b.ResetTimer()
  269 	for i := 0; i < b.N; i++ {
  270 		w := NewWalkway(WalkwayConfig{Fs: fs, Root: "root", WalkFn: walkFn})
  271 
  272 		if err := w.Walk(); err != nil {
  273 			b.Fatal(err)
  274 		}
  275 	}
  276 }