github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/hugofs/walk_test.go (about)

     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  	"github.com/pkg/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  
    85  		bfs := prepare(c)
    86  
    87  		names, err := collectFilenames(bfs, "", "")
    88  
    89  		c.Assert(err, qt.IsNil)
    90  		c.Assert(names, qt.DeepEquals, []string{"a/test.txt", "b/test.txt", "c/test.txt"})
    91  
    92  	})
    93  
    94  	c.Run("Para", func(c *qt.C) {
    95  		bfs := prepare(c)
    96  
    97  		p := para.New(4)
    98  		r, _ := p.Start(context.Background())
    99  
   100  		for i := 0; i < 8; i++ {
   101  			r.Run(func() error {
   102  				_, err := collectFilenames(bfs, "", "")
   103  				if err != nil {
   104  					return err
   105  				}
   106  				fi, err := bfs.Stat("b/test.txt")
   107  				if err != nil {
   108  					return err
   109  				}
   110  				meta := fi.(FileMetaInfo).Meta()
   111  				if meta.Filename == "" {
   112  					return errors.New("fail")
   113  				}
   114  				return nil
   115  
   116  			})
   117  		}
   118  
   119  		c.Assert(r.Wait(), qt.IsNil)
   120  
   121  	})
   122  }
   123  
   124  func skipSymlink() bool {
   125  	if runtime.GOOS != "windows" {
   126  		return false
   127  	}
   128  	if os.Getenv("GITHUB_ACTION") != "" {
   129  		// TODO(bep) figure out why this fails on GitHub Actions.
   130  		return true
   131  	}
   132  	return os.Getenv("CI") == ""
   133  }
   134  
   135  func TestWalkSymbolicLink(t *testing.T) {
   136  	if skipSymlink() {
   137  		t.Skip("Skip; os.Symlink needs administrator rights on Windows")
   138  	}
   139  	c := qt.New(t)
   140  	workDir, clean, err := htesting.CreateTempDir(Os, "hugo-walk-sym")
   141  	c.Assert(err, qt.IsNil)
   142  	defer clean()
   143  	wd, _ := os.Getwd()
   144  	defer func() {
   145  		os.Chdir(wd)
   146  	}()
   147  
   148  	fs := NewBaseFileDecorator(Os)
   149  
   150  	blogDir := filepath.Join(workDir, "blog")
   151  	docsDir := filepath.Join(workDir, "docs")
   152  	blogReal := filepath.Join(blogDir, "real")
   153  	blogRealSub := filepath.Join(blogReal, "sub")
   154  	c.Assert(os.MkdirAll(blogRealSub, 0777), qt.IsNil)
   155  	c.Assert(os.MkdirAll(docsDir, 0777), qt.IsNil)
   156  	afero.WriteFile(fs, filepath.Join(blogRealSub, "a.txt"), []byte("content"), 0777)
   157  	afero.WriteFile(fs, filepath.Join(docsDir, "b.txt"), []byte("content"), 0777)
   158  
   159  	os.Chdir(blogDir)
   160  	c.Assert(os.Symlink("real", "symlinked"), qt.IsNil)
   161  	os.Chdir(blogReal)
   162  	c.Assert(os.Symlink("../real", "cyclic"), qt.IsNil)
   163  	os.Chdir(docsDir)
   164  	c.Assert(os.Symlink("../blog/real/cyclic", "docsreal"), qt.IsNil)
   165  
   166  	t.Run("OS Fs", func(t *testing.T) {
   167  		c := qt.New(t)
   168  
   169  		names, err := collectFilenames(fs, workDir, workDir)
   170  		c.Assert(err, qt.IsNil)
   171  
   172  		c.Assert(names, qt.DeepEquals, []string{"blog/real/sub/a.txt", "docs/b.txt"})
   173  	})
   174  
   175  	t.Run("BasePath Fs", func(t *testing.T) {
   176  		c := qt.New(t)
   177  
   178  		docsFs := afero.NewBasePathFs(fs, docsDir)
   179  
   180  		names, err := collectFilenames(docsFs, "", "")
   181  		c.Assert(err, qt.IsNil)
   182  
   183  		// Note: the docsreal folder is considered cyclic when walking from the root, but this works.
   184  		c.Assert(names, qt.DeepEquals, []string{"b.txt", "docsreal/sub/a.txt"})
   185  	})
   186  }
   187  
   188  func collectFilenames(fs afero.Fs, base, root string) ([]string, error) {
   189  	var names []string
   190  
   191  	walkFn := func(path string, info FileMetaInfo, err error) error {
   192  		if err != nil {
   193  			return err
   194  		}
   195  
   196  		if info.IsDir() {
   197  			return nil
   198  		}
   199  
   200  		filename := info.Meta().Path
   201  		filename = filepath.ToSlash(filename)
   202  
   203  		names = append(names, filename)
   204  
   205  		return nil
   206  	}
   207  
   208  	w := NewWalkway(WalkwayConfig{Fs: fs, BasePath: base, Root: root, WalkFn: walkFn})
   209  
   210  	err := w.Walk()
   211  
   212  	return names, err
   213  }
   214  
   215  func collectFileinfos(fs afero.Fs, base, root string) ([]FileMetaInfo, error) {
   216  	var fis []FileMetaInfo
   217  
   218  	walkFn := func(path string, info FileMetaInfo, err error) error {
   219  		if err != nil {
   220  			return err
   221  		}
   222  
   223  		fis = append(fis, info)
   224  
   225  		return nil
   226  	}
   227  
   228  	w := NewWalkway(WalkwayConfig{Fs: fs, BasePath: base, Root: root, WalkFn: walkFn})
   229  
   230  	err := w.Walk()
   231  
   232  	return fis, err
   233  }
   234  
   235  func BenchmarkWalk(b *testing.B) {
   236  	c := qt.New(b)
   237  	fs := NewBaseFileDecorator(afero.NewMemMapFs())
   238  
   239  	writeFiles := func(dir string, numfiles int) {
   240  		for i := 0; i < numfiles; i++ {
   241  			filename := filepath.Join(dir, fmt.Sprintf("file%d.txt", i))
   242  			c.Assert(afero.WriteFile(fs, filename, []byte("content"), 0777), qt.IsNil)
   243  		}
   244  	}
   245  
   246  	const numFilesPerDir = 20
   247  
   248  	writeFiles("root", numFilesPerDir)
   249  	writeFiles("root/l1_1", numFilesPerDir)
   250  	writeFiles("root/l1_1/l2_1", numFilesPerDir)
   251  	writeFiles("root/l1_1/l2_2", numFilesPerDir)
   252  	writeFiles("root/l1_2", numFilesPerDir)
   253  	writeFiles("root/l1_2/l2_1", numFilesPerDir)
   254  	writeFiles("root/l1_3", numFilesPerDir)
   255  
   256  	walkFn := func(path string, info FileMetaInfo, err error) error {
   257  		if err != nil {
   258  			return err
   259  		}
   260  		if info.IsDir() {
   261  			return nil
   262  		}
   263  
   264  		filename := info.Meta().Filename
   265  		if !strings.HasPrefix(filename, "root") {
   266  			return errors.New(filename)
   267  		}
   268  
   269  		return nil
   270  	}
   271  
   272  	b.ResetTimer()
   273  	for i := 0; i < b.N; i++ {
   274  		w := NewWalkway(WalkwayConfig{Fs: fs, Root: "root", WalkFn: walkFn})
   275  
   276  		if err := w.Walk(); err != nil {
   277  			b.Fatal(err)
   278  		}
   279  	}
   280  }