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 }