github.com/go-maxhub/gremlins@v1.0.1-0.20231227222204-b03a6a1e3e09/core/engine/workdir/workdir_test.go (about) 1 /* 2 * Copyright 2022 The Gremlins 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 workdir_test 18 19 import ( 20 "fmt" 21 "io/fs" 22 "os" 23 "path/filepath" 24 "runtime" 25 "strconv" 26 "sync" 27 "testing" 28 29 "github.com/google/go-cmp/cmp" 30 "github.com/hectane/go-acl" 31 32 "github.com/go-maxhub/gremlins/core/engine/workdir" 33 ) 34 35 func TestCopyFolder(t *testing.T) { 36 srcDir := t.TempDir() 37 populateSrcDir(t, srcDir, 3) 38 wdDir := t.TempDir() 39 40 dealer := workdir.NewCachedDealer(wdDir, srcDir) 41 defer dealer.Clean() 42 43 dstDir, err := dealer.Get("test") 44 if err != nil { 45 t.Fatal(err) 46 } 47 48 err = filepath.Walk(srcDir, checkForDifferentFile(t, srcDir, dstDir)) 49 if err != nil { 50 t.Fatal(err) 51 } 52 } 53 54 func checkForDifferentFile(t *testing.T, srcDir string, dstDir string) func(path string, srcFileInfo fs.FileInfo, err error) error { 55 t.Helper() 56 57 return func(path string, srcFileInfo fs.FileInfo, err error) error { 58 if err != nil { 59 return err 60 } 61 relPath, err := filepath.Rel(srcDir, path) 62 if err != nil { 63 t.Fatal(err) 64 } 65 if relPath == "." { 66 return nil 67 } 68 dstPath := filepath.Join(dstDir, relPath) 69 dstFileInfo, err := os.Lstat(dstPath) 70 if err != nil { 71 t.Fatal(err) 72 } 73 74 sameFile := os.SameFile(dstFileInfo, srcFileInfo) 75 if sameFile { 76 t.Error("expected file to be different, got the same file") 77 } 78 79 if !cmp.Equal(dstFileInfo.Name(), srcFileInfo.Name()) { 80 t.Errorf("expected Name to be %v, got %v", srcFileInfo.Name(), dstFileInfo.Name()) 81 } 82 if !cmp.Equal(dstFileInfo.Mode(), srcFileInfo.Mode()) { 83 t.Errorf(cmp.Diff(srcFileInfo.Mode(), dstFileInfo.Mode())) 84 } 85 86 return nil 87 } 88 } 89 90 func TestCachesFolder(t *testing.T) { 91 t.Run("caches copy folders", func(t *testing.T) { 92 srcDir := t.TempDir() 93 populateSrcDir(t, srcDir, 0) 94 dstDir := t.TempDir() 95 96 mngr := workdir.NewCachedDealer(dstDir, srcDir) 97 defer mngr.Clean() 98 99 firstDir, err := mngr.Get("worker-1") 100 if err != nil { 101 t.Fatal(err) 102 } 103 104 secondDir, err := mngr.Get("worker-1") 105 if err != nil { 106 t.Fatal(err) 107 } 108 109 thirdDir, err := mngr.Get("worker-2") 110 if err != nil { 111 t.Fatal(err) 112 } 113 114 if firstDir != secondDir { 115 t.Errorf("expected dirs to be cached, got %s", cmp.Diff(firstDir, secondDir)) 116 } 117 if firstDir == thirdDir { 118 t.Errorf("expected a new dir to be instanciated") 119 } 120 }) 121 122 t.Run("cleans up all the folders", func(t *testing.T) { 123 srcDir := t.TempDir() 124 populateSrcDir(t, srcDir, 0) 125 dstDir := t.TempDir() 126 127 dealer := workdir.NewCachedDealer(dstDir, srcDir) 128 129 firstDir, err := dealer.Get("worker-1") 130 if err != nil { 131 t.Fatal(err) 132 } 133 134 dealer.Clean() 135 136 secondDir, err := dealer.Get("worker-1") 137 if err != nil { 138 t.Fatal(err) 139 } 140 141 if firstDir == secondDir { 142 t.Errorf("expected manager to be cleaned up") 143 } 144 }) 145 146 t.Run("it works in parallel", func(t *testing.T) { 147 srcDir := t.TempDir() 148 populateSrcDir(t, srcDir, 0) 149 dstDir := t.TempDir() 150 151 dealer := workdir.NewCachedDealer(dstDir, srcDir) 152 defer dealer.Clean() 153 154 foldersLock := sync.Mutex{} 155 var folders []string 156 157 wg := sync.WaitGroup{} 158 wg.Add(10) 159 for i := 0; i < 10; i++ { 160 i := i 161 go func() { 162 defer wg.Done() 163 f, err := dealer.Get(fmt.Sprintf("test-%d", i)) 164 if err != nil { 165 t.Errorf("unexpected error: %s", err) 166 } 167 foldersLock.Lock() 168 defer foldersLock.Unlock() 169 folders = append(folders, f) 170 }() 171 } 172 173 wg.Wait() 174 175 occurred := make(map[string]bool) 176 for _, v := range folders { 177 if occurred[v] == true { 178 t.Fatal("expected values to be unique") 179 } 180 occurred[v] = true 181 } 182 }) 183 } 184 185 func TestErrors(t *testing.T) { 186 t.Run("dstDir is not a path", func(t *testing.T) { 187 srcDir := "not a dir" 188 dstDir := t.TempDir() 189 190 dealer := workdir.NewCachedDealer(dstDir, srcDir) 191 192 _, err := dealer.Get("test") 193 if err == nil { 194 t.Errorf("expected an error") 195 } 196 }) 197 198 t.Run("srcDir is not readable", func(t *testing.T) { 199 srcDir := t.TempDir() 200 err := os.Chmod(srcDir, 0000) 201 clean := os.Chmod 202 if runtime.GOOS == "windows" { 203 err = acl.Chmod(srcDir, 0000) 204 clean = acl.Chmod 205 } 206 if err != nil { 207 t.Fatal(err) 208 } 209 defer func(d string) { 210 _ = clean(d, 0700) 211 }(srcDir) 212 213 dstDir := t.TempDir() 214 215 mngr := workdir.NewCachedDealer(dstDir, srcDir) 216 217 _, err = mngr.Get("test") 218 if err == nil { 219 t.Errorf("expected an error") 220 } 221 }) 222 223 t.Run("dstDir is not writeable", func(t *testing.T) { 224 srcDir := t.TempDir() 225 dstDir := t.TempDir() 226 err := os.Chmod(dstDir, 0000) 227 clean := os.Chmod 228 if runtime.GOOS == "windows" { 229 err = acl.Chmod(dstDir, 0000) 230 clean = acl.Chmod 231 } 232 if err != nil { 233 t.Fatal(err) 234 } 235 defer func(d string) { 236 _ = clean(d, 0700) 237 }(dstDir) 238 239 dealer := workdir.NewCachedDealer(dstDir, srcDir) 240 241 _, err = dealer.Get("test") 242 if err == nil { 243 t.Errorf("expected an error") 244 } 245 }) 246 } 247 248 func TestWorkDirReturnsTheRootWorkingDir(t *testing.T) { 249 srcDir := t.TempDir() 250 populateSrcDir(t, srcDir, 0) 251 wdDir := t.TempDir() 252 253 dealer := workdir.NewCachedDealer(wdDir, srcDir) 254 defer dealer.Clean() 255 256 rootWorkingDir := dealer.WorkDir() 257 if rootWorkingDir != wdDir { 258 t.Errorf("expected working dir to be %s, got: %s", wdDir, rootWorkingDir) 259 } 260 } 261 262 func BenchmarkDealerGet(b *testing.B) { 263 srcDir := b.TempDir() 264 populateSrcDir(b, srcDir, 5) 265 266 var wIdx = -1 267 var workers = []int{1, 2, 3, 4, 5, 6, 7, 8} 268 269 getNextWorker := func() string { 270 wIdx++ 271 if wIdx > 7 { 272 wIdx = 0 273 } 274 275 return "worker-" + strconv.Itoa(workers[wIdx]) 276 } 277 278 for i := 0; i < b.N; i++ { 279 runTest(b, srcDir, getNextWorker()) 280 } 281 } 282 283 func runTest(b *testing.B, srcDir, workerName string) { 284 b.Helper() 285 dstDir := b.TempDir() 286 287 mngr := workdir.NewCachedDealer(dstDir, srcDir) 288 defer mngr.Clean() 289 290 _, err := mngr.Get(workerName) 291 if err != nil { 292 b.Fatal(err) 293 } 294 } 295 296 type testT interface { 297 Helper() 298 Fatal(args ...any) 299 } 300 301 func populateSrcDir(t testT, srcDir string, depth int) { 302 t.Helper() 303 if depth == 0 { 304 return 305 } 306 307 for i := 0; i < 10; i++ { 308 dirName := filepath.Join(srcDir, fmt.Sprintf("srcdir-%d", i)) 309 err := os.Mkdir(dirName, 0700) 310 if err != nil { 311 t.Fatal(err) 312 } 313 populateSrcDir(t, dirName, depth-1) 314 } 315 316 for i := 0; i < 10; i++ { 317 fileName := filepath.Join(srcDir, fmt.Sprintf("srcfile-%d", i)) 318 err := os.WriteFile(fileName, getFileBytes(), 0400) 319 if err != nil { 320 t.Fatal(err) 321 } 322 } 323 } 324 325 func getFileBytes() []byte { 326 b, _ := os.ReadFile("testdata/filetocopy_go") 327 328 return b 329 }