github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/bin/make_test_files.go (about)

     1  // +build ignore
     2  
     3  // Build a directory structure with the required number of files in
     4  //
     5  // Run with go run make_test_files.go [flag] <directory>
     6  package main
     7  
     8  import (
     9  	cryptrand "crypto/rand"
    10  	"flag"
    11  	"io"
    12  	"log"
    13  	"math/rand"
    14  	"os"
    15  	"path/filepath"
    16  )
    17  
    18  var (
    19  	// Flags
    20  	numberOfFiles            = flag.Int("n", 1000, "Number of files to create")
    21  	averageFilesPerDirectory = flag.Int("files-per-directory", 10, "Average number of files per directory")
    22  	maxDepth                 = flag.Int("max-depth", 10, "Maximum depth of directory heirachy")
    23  	minFileSize              = flag.Int64("min-size", 0, "Minimum size of file to create")
    24  	maxFileSize              = flag.Int64("max-size", 100, "Maximum size of files to create")
    25  	minFileNameLength        = flag.Int("min-name-length", 4, "Minimum size of file to create")
    26  	maxFileNameLength        = flag.Int("max-name-length", 12, "Maximum size of files to create")
    27  
    28  	directoriesToCreate int
    29  	totalDirectories    int
    30  	fileNames           = map[string]struct{}{} // keep a note of which file name we've used already
    31  )
    32  
    33  // randomString create a random string for test purposes
    34  func randomString(n int) string {
    35  	const (
    36  		vowel     = "aeiou"
    37  		consonant = "bcdfghjklmnpqrstvwxyz"
    38  		digit     = "0123456789"
    39  	)
    40  	pattern := []string{consonant, vowel, consonant, vowel, consonant, vowel, consonant, digit}
    41  	out := make([]byte, n)
    42  	p := 0
    43  	for i := range out {
    44  		source := pattern[p]
    45  		p = (p + 1) % len(pattern)
    46  		out[i] = source[rand.Intn(len(source))]
    47  	}
    48  	return string(out)
    49  }
    50  
    51  // fileName creates a unique random file or directory name
    52  func fileName() (name string) {
    53  	for {
    54  		length := rand.Intn(*maxFileNameLength-*minFileNameLength) + *minFileNameLength
    55  		name = randomString(length)
    56  		if _, found := fileNames[name]; !found {
    57  			break
    58  		}
    59  	}
    60  	fileNames[name] = struct{}{}
    61  	return name
    62  }
    63  
    64  // dir is a directory in the directory heirachy being built up
    65  type dir struct {
    66  	name     string
    67  	depth    int
    68  	children []*dir
    69  	parent   *dir
    70  }
    71  
    72  // Create a random directory heirachy under d
    73  func (d *dir) createDirectories() {
    74  	for totalDirectories < directoriesToCreate {
    75  		newDir := &dir{
    76  			name:   fileName(),
    77  			depth:  d.depth + 1,
    78  			parent: d,
    79  		}
    80  		d.children = append(d.children, newDir)
    81  		totalDirectories++
    82  		switch rand.Intn(4) {
    83  		case 0:
    84  			if d.depth < *maxDepth {
    85  				newDir.createDirectories()
    86  			}
    87  		case 1:
    88  			return
    89  		}
    90  	}
    91  	return
    92  }
    93  
    94  // list the directory heirachy
    95  func (d *dir) list(path string, output []string) []string {
    96  	dirPath := filepath.Join(path, d.name)
    97  	output = append(output, dirPath)
    98  	for _, subDir := range d.children {
    99  		output = subDir.list(dirPath, output)
   100  	}
   101  	return output
   102  }
   103  
   104  // writeFile writes a random file at dir/name
   105  func writeFile(dir, name string) {
   106  	err := os.MkdirAll(dir, 0777)
   107  	if err != nil {
   108  		log.Fatalf("Failed to make directory %q: %v", dir, err)
   109  	}
   110  	path := filepath.Join(dir, name)
   111  	fd, err := os.Create(path)
   112  	if err != nil {
   113  		log.Fatalf("Failed to open file %q: %v", path, err)
   114  	}
   115  	size := rand.Int63n(*maxFileSize-*minFileSize) + *minFileSize
   116  	_, err = io.CopyN(fd, cryptrand.Reader, size)
   117  	if err != nil {
   118  		log.Fatalf("Failed to write %v bytes to file %q: %v", size, path, err)
   119  	}
   120  	err = fd.Close()
   121  	if err != nil {
   122  		log.Fatalf("Failed to close file %q: %v", path, err)
   123  	}
   124  }
   125  
   126  func main() {
   127  	flag.Parse()
   128  	args := flag.Args()
   129  	if len(args) != 1 {
   130  		log.Fatalf("Require 1 directory argument")
   131  	}
   132  	outputDirectory := args[0]
   133  	log.Printf("Output dir %q", outputDirectory)
   134  
   135  	directoriesToCreate = *numberOfFiles / *averageFilesPerDirectory
   136  	log.Printf("directoriesToCreate %v", directoriesToCreate)
   137  	root := &dir{name: outputDirectory, depth: 1}
   138  	for totalDirectories < directoriesToCreate {
   139  		root.createDirectories()
   140  	}
   141  	dirs := root.list("", []string{})
   142  	for i := 0; i < *numberOfFiles; i++ {
   143  		dir := dirs[rand.Intn(len(dirs))]
   144  		writeFile(dir, fileName())
   145  	}
   146  }