github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/testutil/testdir.go (about)

     1  package testutil
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/markusbkk/elvish/pkg/env"
     9  )
    10  
    11  // TempDir creates a temporary directory for testing that will be removed
    12  // after the test finishes. It is different from testing.TB.TempDir in that it
    13  // resolves symlinks in the path of the directory.
    14  //
    15  // It panics if the test directory cannot be created or symlinks cannot be
    16  // resolved. It is only suitable for use in tests.
    17  func TempDir(c Cleanuper) string {
    18  	dir, err := os.MkdirTemp("", "elvishtest.")
    19  	if err != nil {
    20  		panic(err)
    21  	}
    22  	dir, err = filepath.EvalSymlinks(dir)
    23  	if err != nil {
    24  		panic(err)
    25  	}
    26  	c.Cleanup(func() {
    27  		err := os.RemoveAll(dir)
    28  		if err != nil {
    29  			fmt.Fprintf(os.Stderr, "failed to remove temp dir %s: %v\n", dir, err)
    30  		}
    31  	})
    32  	return dir
    33  }
    34  
    35  // TempHome is equivalent to Setenv(c, env.HOME, TempDir(c))
    36  func TempHome(c Cleanuper) string {
    37  	return Setenv(c, env.HOME, TempDir(c))
    38  }
    39  
    40  // Chdir changes into a directory, and restores the original working directory
    41  // when a test finishes. It returns the directory for easier chaining.
    42  func Chdir(c Cleanuper, dir string) string {
    43  	oldWd, err := os.Getwd()
    44  	if err != nil {
    45  		panic(err)
    46  	}
    47  	Must(os.Chdir(dir))
    48  	c.Cleanup(func() {
    49  		Must(os.Chdir(oldWd))
    50  	})
    51  	return dir
    52  }
    53  
    54  // InTempDir is equivalent to Chdir(c, TempDir(c)).
    55  func InTempDir(c Cleanuper) string {
    56  	return Chdir(c, TempDir(c))
    57  }
    58  
    59  // InTempHome is equivalent to Setenv(c, env.HOME, InTempDir(c))
    60  func InTempHome(c Cleanuper) string {
    61  	return Setenv(c, env.HOME, InTempDir(c))
    62  }
    63  
    64  // Dir describes the layout of a directory. The keys of the map represent
    65  // filenames. Each value is either a string (for the content of a regular file
    66  // with permission 0644), a File, or a Dir.
    67  type Dir map[string]interface{}
    68  
    69  // File describes a file to create.
    70  type File struct {
    71  	Perm    os.FileMode
    72  	Content string
    73  }
    74  
    75  // ApplyDir creates the given filesystem layout in the current directory.
    76  func ApplyDir(dir Dir) {
    77  	applyDir(dir, "")
    78  }
    79  
    80  func applyDir(dir Dir, prefix string) {
    81  	for name, file := range dir {
    82  		path := filepath.Join(prefix, name)
    83  		switch file := file.(type) {
    84  		case string:
    85  			Must(os.WriteFile(path, []byte(file), 0644))
    86  		case File:
    87  			Must(os.WriteFile(path, []byte(file.Content), file.Perm))
    88  		case Dir:
    89  			Must(os.MkdirAll(path, 0755))
    90  			applyDir(file, path)
    91  		default:
    92  			panic(fmt.Sprintf("file is neither string, Dir, or Symlink: %v", file))
    93  		}
    94  	}
    95  }