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  }