github.com/avfs/avfs@v0.33.1-0.20240303173310-c6ba67c33eb7/rndtree.go (about)

     1  //
     2  //  Copyright 2020 The AVFS authors
     3  //
     4  //  Licensed under the Apache License, Version 2.0 (the "License");
     5  //  you may not use this file except in compliance with the License.
     6  //  You may obtain a copy of the License at
     7  //
     8  //  	http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  //  Unless required by applicable law or agreed to in writing, software
    11  //  distributed under the License is distributed on an "AS IS" BASIS,
    12  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  //  See the License for the specific language governing permissions and
    14  //  limitations under the License.
    15  //
    16  
    17  package avfs
    18  
    19  import (
    20  	"math/rand"
    21  	"strconv"
    22  )
    23  
    24  // RndTreeOpts defines the parameters to generate a random file system tree
    25  // of directories, files and symbolic links.
    26  type RndTreeOpts struct {
    27  	NbDirs      int // NbDirs is the number of directories.
    28  	NbFiles     int // NbFiles is the number of files.
    29  	NbSymlinks  int // NbSymlinks is the number of symbolic links.
    30  	MaxFileSize int // MaxFileSize is maximum size of a file.
    31  	MaxDepth    int // MaxDepth is the maximum depth of the tree.
    32  }
    33  
    34  // RndTreeDir contains parameters to create a directory.
    35  type RndTreeDir struct {
    36  	Name  string
    37  	Depth int
    38  }
    39  
    40  // RndTreeFile contains parameters to create a file.
    41  type RndTreeFile struct {
    42  	Name string
    43  	Size int
    44  }
    45  
    46  // RndTreeSymLink contains parameters to create a symbolic link.
    47  type RndTreeSymLink struct {
    48  	OldName, NewName string
    49  }
    50  
    51  // RndTree is a random file system tree generator of directories, files and symbolic links.
    52  type RndTree struct {
    53  	vfs         VFSBase           // vfs is the virtual file system.
    54  	rnd         *rand.Rand        // rnd is a source of random numbers.
    55  	dirs        []*RndTreeDir     // Dirs contains all directories.
    56  	files       []*RndTreeFile    // Files contains all files.
    57  	symLinks    []*RndTreeSymLink // SymLinks contains all symbolic links.
    58  	RndTreeOpts                   // RndTreeOpts regroups the options of the tree.
    59  }
    60  
    61  // NewRndTree returns a new random tree generator.
    62  func NewRndTree(vfs VFSBase, opts *RndTreeOpts) *RndTree {
    63  	if opts.NbDirs < 0 {
    64  		opts.NbDirs = 0
    65  	}
    66  
    67  	if opts.NbFiles < 0 {
    68  		opts.NbFiles = 0
    69  	}
    70  
    71  	if opts.NbSymlinks < 0 {
    72  		opts.NbSymlinks = 0
    73  	}
    74  
    75  	if opts.MaxDepth < 0 {
    76  		opts.MaxDepth = 0
    77  	}
    78  
    79  	if opts.MaxFileSize < 0 {
    80  		opts.MaxFileSize = 0
    81  	}
    82  
    83  	rt := &RndTree{
    84  		vfs: vfs,
    85  		rnd: rand.New(rand.NewSource(42)),
    86  		RndTreeOpts: RndTreeOpts{
    87  			NbDirs:      opts.NbDirs,
    88  			NbFiles:     opts.NbFiles,
    89  			NbSymlinks:  opts.NbSymlinks,
    90  			MaxFileSize: opts.MaxFileSize,
    91  			MaxDepth:    opts.MaxDepth,
    92  		},
    93  	}
    94  
    95  	return rt
    96  }
    97  
    98  // GenTree generates a random tree and populates RndTree.Dirs, RndTree.Files and RndTree.SymLinks.
    99  func (rt *RndTree) GenTree() {
   100  	nameIdx := 0
   101  	name := func(prefix string) string {
   102  		nameIdx++
   103  
   104  		return prefix + "-" + strconv.Itoa(nameIdx)
   105  	}
   106  
   107  	if rt.dirs != nil {
   108  		return
   109  	}
   110  
   111  	nbDirs := rt.NbDirs
   112  	dirs := make([]*RndTreeDir, nbDirs)
   113  
   114  	parents := make([]*RndTreeDir, 1, 10)
   115  	parents[0] = &RndTreeDir{}
   116  
   117  	for i := 0; i < nbDirs; i++ {
   118  		parent := parents[rand.Intn(len(parents))]
   119  		path := parent.Name + "/" + name("dir")
   120  		depth := parent.Depth + 1
   121  
   122  		dir := &RndTreeDir{Name: path, Depth: depth}
   123  		dirs[i] = dir
   124  
   125  		if depth < rt.MaxDepth {
   126  			parents = append(parents, dir)
   127  		}
   128  	}
   129  
   130  	rt.dirs = dirs
   131  
   132  	if rt.NbFiles == 0 {
   133  		return
   134  	}
   135  
   136  	nbParents := len(parents)
   137  	nbFiles := rt.NbFiles
   138  	files := make([]*RndTreeFile, nbFiles)
   139  
   140  	for i := 0; i < nbFiles; i++ {
   141  		parent := parents[rand.Intn(nbParents)]
   142  		fileName := parent.Name + "/" + name("file")
   143  
   144  		size := 0
   145  		if rt.MaxFileSize > 0 {
   146  			size = rand.Intn(rt.MaxFileSize)
   147  		}
   148  
   149  		file := &RndTreeFile{Name: fileName, Size: size}
   150  		files[i] = file
   151  	}
   152  
   153  	rt.files = files
   154  
   155  	if !rt.vfs.HasFeature(FeatSymlink) {
   156  		return
   157  	}
   158  
   159  	nbSymlinks := rt.NbSymlinks
   160  	symLinks := make([]*RndTreeSymLink, nbSymlinks)
   161  
   162  	for i := 0; i < nbSymlinks; i++ {
   163  		oldName := files[rand.Intn(nbFiles)].Name
   164  		newDir := parents[rand.Intn(nbParents)].Name
   165  		newName := newDir + "/" + name("symlink")
   166  
   167  		sl := &RndTreeSymLink{OldName: oldName, NewName: newName}
   168  		symLinks[i] = sl
   169  	}
   170  
   171  	rt.symLinks = symLinks
   172  }
   173  
   174  // CreateDirs creates random directories.
   175  func (rt *RndTree) CreateDirs(baseDir string) error {
   176  	vfs := rt.vfs
   177  
   178  	rt.GenTree()
   179  
   180  	for _, dir := range rt.dirs {
   181  		path := vfs.Join(baseDir, dir.Name)
   182  
   183  		err := vfs.MkdirAll(path, DefaultDirPerm)
   184  		if err != nil {
   185  			return err
   186  		}
   187  	}
   188  
   189  	return nil
   190  }
   191  
   192  // CreateFiles creates random files.
   193  func (rt *RndTree) CreateFiles(baseDir string) error {
   194  	err := rt.CreateDirs(baseDir)
   195  	if err != nil {
   196  		return err
   197  	}
   198  
   199  	buf := make([]byte, rt.MaxFileSize)
   200  	rt.rnd.Read(buf)
   201  
   202  	vfs := rt.vfs
   203  
   204  	for _, file := range rt.files {
   205  		path := vfs.Join(baseDir, file.Name)
   206  
   207  		err = vfs.WriteFile(path, buf[:file.Size], DefaultFilePerm)
   208  		if err != nil {
   209  			return err
   210  		}
   211  	}
   212  
   213  	return nil
   214  }
   215  
   216  // CreateSymlinks creates random symbolic links.
   217  func (rt *RndTree) CreateSymlinks(baseDir string) error {
   218  	err := rt.CreateFiles(baseDir)
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	vfs := rt.vfs
   224  	if !vfs.HasFeature(FeatSymlink) {
   225  		return nil
   226  	}
   227  
   228  	for _, symlink := range rt.symLinks {
   229  		oldPath := vfs.Join(baseDir, symlink.OldName)
   230  		newPath := vfs.Join(baseDir, symlink.NewName)
   231  
   232  		err = vfs.Symlink(oldPath, newPath)
   233  		if err != nil {
   234  			return err
   235  		}
   236  	}
   237  
   238  	return nil
   239  }
   240  
   241  // CreateTree creates a random tree structure.
   242  func (rt *RndTree) CreateTree(baseDir string) error {
   243  	return rt.CreateSymlinks(baseDir)
   244  }
   245  
   246  func (rt *RndTree) Dirs() []*RndTreeDir {
   247  	return rt.dirs
   248  }
   249  
   250  func (rt *RndTree) Files() []*RndTreeFile {
   251  	return rt.files
   252  }
   253  
   254  func (rt *RndTree) SymLinks() []*RndTreeSymLink {
   255  	return rt.symLinks
   256  }