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 }