github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/testutils/tempdir/temp_dir_fixture.go (about)

     1  package tempdir
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"regexp"
     7  	"runtime"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/tilt-dev/wmclient/pkg/os/temp"
    12  )
    13  
    14  type TempDirFixture struct {
    15  	t      testing.TB
    16  	dir    *temp.TempDir
    17  	oldDir string
    18  }
    19  
    20  // everything not listed in this character class will get replaced by _, so that it's a safe filename
    21  var sanitizeForFilenameRe = regexp.MustCompile("[^a-zA-Z0-9.]")
    22  
    23  func SanitizeFileName(name string) string {
    24  	return sanitizeForFilenameRe.ReplaceAllString(name, "_")
    25  }
    26  
    27  func NewTempDirFixture(t testing.TB) *TempDirFixture {
    28  	dir, err := temp.NewDir(SanitizeFileName(t.Name()))
    29  	if err != nil {
    30  		t.Fatalf("Error making temp dir: %v", err)
    31  	}
    32  
    33  	ret := &TempDirFixture{
    34  		t:   t,
    35  		dir: dir,
    36  	}
    37  
    38  	t.Cleanup(ret.tearDown)
    39  
    40  	return ret
    41  }
    42  
    43  func (f *TempDirFixture) T() testing.TB {
    44  	return f.t
    45  }
    46  
    47  func (f *TempDirFixture) Path() string {
    48  	return f.dir.Path()
    49  }
    50  
    51  func (f *TempDirFixture) Chdir() {
    52  	cwd, err := os.Getwd()
    53  	if err != nil {
    54  		f.t.Fatal(err)
    55  	}
    56  
    57  	f.oldDir = cwd
    58  
    59  	err = os.Chdir(f.Path())
    60  	if err != nil {
    61  		f.t.Fatal(err)
    62  	}
    63  }
    64  
    65  func (f *TempDirFixture) JoinPath(path ...string) string {
    66  	p := []string{}
    67  	isAbs := len(path) > 0 && filepath.IsAbs(path[0])
    68  	if isAbs {
    69  		if !strings.HasPrefix(path[0], f.Path()) {
    70  			f.t.Fatalf("Path outside fixture tempdir are forbidden: %s", path[0])
    71  		}
    72  	} else {
    73  		p = append(p, f.Path())
    74  	}
    75  
    76  	p = append(p, path...)
    77  	return filepath.Join(p...)
    78  }
    79  
    80  func (f *TempDirFixture) JoinPaths(paths []string) []string {
    81  	joined := make([]string, len(paths))
    82  	for i, p := range paths {
    83  		joined[i] = f.JoinPath(p)
    84  	}
    85  	return joined
    86  }
    87  
    88  // Returns the full path to the file written.
    89  func (f *TempDirFixture) WriteFile(path string, contents string) string {
    90  	fullPath := f.JoinPath(path)
    91  	base := filepath.Dir(fullPath)
    92  	err := os.MkdirAll(base, os.FileMode(0777))
    93  	if err != nil {
    94  		f.t.Fatal(err)
    95  	}
    96  	err = os.WriteFile(fullPath, []byte(contents), os.FileMode(0777))
    97  	if err != nil {
    98  		f.t.Fatal(err)
    99  	}
   100  	return fullPath
   101  }
   102  
   103  // Returns the full path to the file written.
   104  func (f *TempDirFixture) CopyFile(originalPath, newPath string) {
   105  	contents, err := os.ReadFile(originalPath)
   106  	if err != nil {
   107  		f.t.Fatal(err)
   108  	}
   109  	f.WriteFile(newPath, string(contents))
   110  }
   111  
   112  // Read the file.
   113  func (f *TempDirFixture) ReadFile(path string) string {
   114  	fullPath := f.JoinPath(path)
   115  	contents, err := os.ReadFile(fullPath)
   116  	if err != nil {
   117  		f.t.Fatal(err)
   118  	}
   119  	return string(contents)
   120  }
   121  
   122  func (f *TempDirFixture) WriteSymlink(linkContents, destPath string) {
   123  	fullDestPath := f.JoinPath(destPath)
   124  	err := os.MkdirAll(filepath.Dir(fullDestPath), os.FileMode(0777))
   125  	if err != nil {
   126  		f.t.Fatal(err)
   127  	}
   128  	err = os.Symlink(linkContents, fullDestPath)
   129  	if err != nil {
   130  		f.t.Fatal(err)
   131  	}
   132  }
   133  
   134  func (f *TempDirFixture) MkdirAll(path string) {
   135  	fullPath := f.JoinPath(path)
   136  	err := os.MkdirAll(fullPath, os.FileMode(0777))
   137  	if err != nil {
   138  		f.t.Fatal(err)
   139  	}
   140  }
   141  
   142  func (f *TempDirFixture) TouchFiles(paths []string) {
   143  	for _, p := range paths {
   144  		f.WriteFile(p, "")
   145  	}
   146  }
   147  
   148  func (f *TempDirFixture) Rm(pathInRepo string) {
   149  	fullPath := f.JoinPath(pathInRepo)
   150  	err := os.RemoveAll(fullPath)
   151  	if err != nil {
   152  		f.t.Fatal(err)
   153  	}
   154  }
   155  
   156  func (f *TempDirFixture) NewFile(prefix string) (*os.File, error) {
   157  	return os.CreateTemp(f.dir.Path(), prefix)
   158  }
   159  
   160  func (f *TempDirFixture) TempDir(prefix string) string {
   161  	name, err := os.MkdirTemp(f.dir.Path(), prefix)
   162  	if err != nil {
   163  		f.t.Fatal(err)
   164  	}
   165  	return name
   166  }
   167  
   168  func (f *TempDirFixture) tearDown() {
   169  	if f.oldDir != "" {
   170  		err := os.Chdir(f.oldDir)
   171  		if err != nil {
   172  			f.t.Fatal(err)
   173  		}
   174  	}
   175  
   176  	err := f.dir.TearDown()
   177  	if err != nil && runtime.GOOS == "windows" &&
   178  		(strings.Contains(err.Error(), "The process cannot access the file") ||
   179  			strings.Contains(err.Error(), "Access is denied")) {
   180  		// NOTE(nick): I'm not convinced that this is a real problem.
   181  		// I think it might just be clean up of file notification I/O.
   182  	} else if err != nil {
   183  		f.t.Fatal(err)
   184  	}
   185  }