code.flowtr.dev/mirrors/u-root@v1.0.0/cmds/cp/cp_test.go (about)

     1  // Copyright 2016 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // created by Manoel Vilela <manoel_vilela@engineer.com>
     6  
     7  package main
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"math/rand"
    13  	"os"
    14  	"path/filepath"
    15  	"reflect"
    16  	"testing"
    17  	"time"
    18  )
    19  
    20  var (
    21  	// simple label for src and dst
    22  	indentifier = time.Now().Format(time.Kitchen)
    23  )
    24  
    25  const (
    26  	maxSizeFile = buffSize * 2
    27  	rangeRand   = 100
    28  	maxDirDepth = 5
    29  	maxFiles    = 5
    30  )
    31  
    32  // resetFlags is used to reset the cp flags to default
    33  func resetFlags() {
    34  	nwork = 1
    35  	recursive = false
    36  	ask = false
    37  	force = false
    38  	verbose = false
    39  	symlink = false
    40  }
    41  
    42  // randomFile create a random file with random content
    43  func randomFile(fpath, prefix string) (*os.File, error) {
    44  	f, err := ioutil.TempFile(fpath, prefix)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	// generate random content for files
    49  	bytes := []byte{}
    50  	for i := 0; i < rand.Intn(maxSizeFile); i++ {
    51  		bytes = append(bytes, byte(i))
    52  	}
    53  	f.Write(bytes)
    54  
    55  	return f, nil
    56  }
    57  
    58  // createFilesTree create a random files tree
    59  func createFilesTree(root string, maxDepth, depth int) error {
    60  	// create more one dir if don't achieve the maxDepth
    61  	if depth < maxDepth {
    62  		newDir, err := ioutil.TempDir(root, fmt.Sprintf("cpdir_%d_", depth))
    63  		if err != nil {
    64  			return err
    65  		}
    66  
    67  		if err = createFilesTree(newDir, maxDepth, depth+1); err != nil {
    68  			return err
    69  		}
    70  	}
    71  	// generate random files
    72  	for i := 0; i < maxFiles; i++ {
    73  		f, err := randomFile(root, fmt.Sprintf("cpfile_%d_", i))
    74  		if err != nil {
    75  			return err
    76  		}
    77  		f.Close()
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  // readDirs get the path and fname each file of dir pathToRead
    84  // set on that structure fname:[fpath1, fpath2]
    85  // use that to compare if the files with same name has same content
    86  func readDirs(pathToRead string, mapFiles map[string][]string) error {
    87  	pathFiles, err := ioutil.ReadDir(pathToRead)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	for _, file := range pathFiles {
    92  		fname := file.Name()
    93  		_, exists := mapFiles[fname]
    94  		fpath := filepath.Join(pathToRead, fname)
    95  		if !exists {
    96  			slc := []string{fpath}
    97  			mapFiles[fname] = slc
    98  		} else {
    99  			mapFiles[fname] = append(mapFiles[fname], fpath)
   100  		}
   101  	}
   102  	return err
   103  }
   104  
   105  // isEqualFile compare two files by checksum
   106  func isEqualFile(f1, f2 *os.File) (bool, error) {
   107  	bytes1, err := ioutil.ReadAll(f1)
   108  	if err != nil {
   109  		return false, fmt.Errorf("Failed to read the file %v: %v", f1, err)
   110  	}
   111  	bytes2, err := ioutil.ReadAll(f2)
   112  	if err != nil {
   113  		return false, fmt.Errorf("Failed to read the file %v: %v", f2, err)
   114  	}
   115  
   116  	if !reflect.DeepEqual(bytes1, bytes2) {
   117  		return false, nil
   118  	}
   119  
   120  	return true, nil
   121  }
   122  
   123  // isEqualTree compare the content between of src and dst paths
   124  func isEqualTree(src, dst string) (bool, error) {
   125  	mapFiles := map[string][]string{}
   126  	if err := readDirs(src, mapFiles); err != nil {
   127  		return false, fmt.Errorf("cannot read dir %v: %v", src, err)
   128  	}
   129  	if err := readDirs(dst, mapFiles); err != nil {
   130  		return false, fmt.Errorf("cannot read dir %v: %v", dst, err)
   131  	}
   132  
   133  	equalTree := true
   134  	for _, files := range mapFiles {
   135  		if len(files) < 2 {
   136  			return false, fmt.Errorf("insufficient files in readDirs(): expected at least 2, got %v", len(files))
   137  		}
   138  		fpath1, fpath2 := files[0], files[1]
   139  		file1, err := os.Open(fpath1)
   140  		if err != nil {
   141  			return false, fmt.Errorf("cannot open file %v: %v", fpath1, err)
   142  		}
   143  		file2, err := os.Open(fpath2)
   144  		if err != nil {
   145  			return false, fmt.Errorf("cannot open file %v: %v", fpath2, err)
   146  		}
   147  
   148  		stat1, err := file1.Stat()
   149  		if err != nil {
   150  			return false, fmt.Errorf("cannot stat file %v: %v", file1, err)
   151  		}
   152  		stat2, err := file2.Stat()
   153  		if err != nil {
   154  			return false, fmt.Errorf("cannot stat file %v: %v", file2, err)
   155  
   156  		}
   157  		if stat1.IsDir() && stat2.IsDir() {
   158  			equalDirs, err := isEqualTree(fpath1, fpath2)
   159  			if err != nil {
   160  				return false, err
   161  			}
   162  			equalTree = equalTree && equalDirs
   163  
   164  		} else {
   165  			equalFiles, err := isEqualFile(file1, file2)
   166  			if err != nil {
   167  				return false, err
   168  			}
   169  			equalTree = equalTree && equalFiles
   170  
   171  		}
   172  		if !equalTree {
   173  			break
   174  		}
   175  		file1.Close()
   176  		file2.Close()
   177  
   178  	}
   179  
   180  	return equalTree, nil
   181  
   182  }
   183  
   184  // TestCpsSimple make a simple test for copy file-to-file
   185  // cmd-line equivalent: cp file file-copy
   186  func TestCpSimple(t *testing.T) {
   187  	tempDir, err := ioutil.TempDir("", "TestCpSimple")
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	defer os.RemoveAll(tempDir)
   192  
   193  	srcPrefix := fmt.Sprintf("cpfile_%v_src", indentifier)
   194  	f, err := randomFile(tempDir, srcPrefix)
   195  	if err != nil {
   196  		t.Fatalf("cannot create a random file: %v", err)
   197  	}
   198  	defer f.Close()
   199  	srcFpath := f.Name()
   200  
   201  	dstFname := fmt.Sprintf("cpfile_%v_dst_copied", indentifier)
   202  	dstFpath := filepath.Join(tempDir, dstFname)
   203  
   204  	if err := copyFile(srcFpath, dstFpath, false); err != nil {
   205  		t.Fatalf("copyFile %v -> %v failed: %v", srcFpath, dstFpath, err)
   206  	}
   207  	s, err := os.Open(srcFpath)
   208  	if err != nil {
   209  		t.Fatalf("cannot open the file %v", srcFpath)
   210  	}
   211  	defer s.Close()
   212  	d, err := os.Open(dstFpath)
   213  	if err != nil {
   214  		t.Fatalf("cannot open the file %v", dstFpath)
   215  	}
   216  	defer d.Close()
   217  	if equal, err := isEqualFile(s, d); !equal || err != nil {
   218  		t.Fatalf("checksum are different; copies failed %q -> %q: %v", srcFpath, dstFpath, err)
   219  	}
   220  }
   221  
   222  // TestCpRecursive tests the recursive mode copy
   223  // Copy dir hierarchies src-dir to dst-dir
   224  // whose src-dir and dst-dir already exists
   225  // cmd-line equivalent: $ cp -R src-dir/ dst-dir/
   226  func TestCpRecursive(t *testing.T) {
   227  	recursive = true
   228  	defer resetFlags()
   229  	tempDir, err := ioutil.TempDir("", "TestCpRecursive")
   230  	if err != nil {
   231  		t.Fatal(err)
   232  	}
   233  	defer os.RemoveAll(tempDir)
   234  
   235  	srcPrefix := fmt.Sprintf("TestCpSrc_%v_", indentifier)
   236  	dstPrefix := fmt.Sprintf("TestCpDst_%v_copied", indentifier)
   237  	srcTest, err := ioutil.TempDir(tempDir, srcPrefix)
   238  	if err != nil {
   239  		t.Fatalf("Failed on build directory %q: %v\n", srcTest, err)
   240  	}
   241  	if err = createFilesTree(srcTest, maxDirDepth, 0); err != nil {
   242  		t.Fatalf("cannot create files tree on directory %q: %v", srcTest, err)
   243  	}
   244  
   245  	dstTest, err := ioutil.TempDir(tempDir, dstPrefix)
   246  	if err != nil {
   247  		t.Fatalf("Failed on build directory %q: %v\n", dstTest, err)
   248  	}
   249  	if err := copyFile(srcTest, dstTest, false); err != nil {
   250  		t.Fatalf("copyFile %q -> %q failed: %v", srcTest, dstTest, err)
   251  	}
   252  
   253  	if equal, err := isEqualTree(srcTest, dstTest); !equal || err != nil {
   254  		t.Fatalf("The copy %q -> %q failed, trees are different: %v", srcTest, dstTest, err)
   255  	}
   256  }
   257  
   258  // whose src-dir exists but dst-dir no
   259  // cmd-line equivalent: $ cp -R some-dir/ new-dir/
   260  func TestCpRecursiveNew(t *testing.T) {
   261  	recursive = true
   262  	defer resetFlags()
   263  	tempDir, err := ioutil.TempDir("", "TestCpRecursiveNew")
   264  	if err != nil {
   265  		t.Fatal(err)
   266  	}
   267  	defer os.RemoveAll(tempDir)
   268  
   269  	srcPrefix := fmt.Sprintf("TestCpSrc_%v_", indentifier)
   270  	dstPrefix := fmt.Sprintf("TestCpDst_%v_new", indentifier)
   271  	srcTest, err := ioutil.TempDir(tempDir, srcPrefix)
   272  	if err != nil {
   273  		t.Fatalf("failed on build tmp directory %q: %v\n", srcPrefix, err)
   274  	}
   275  
   276  	if err = createFilesTree(srcTest, maxDirDepth, 0); err != nil {
   277  		t.Fatalf("cannot create files tree on directory %q: %v", srcTest, err)
   278  	}
   279  
   280  	dstTest := filepath.Join(tempDir, dstPrefix)
   281  	copyFile(srcTest, dstTest, false)
   282  	isEqual, err := isEqualTree(srcTest, dstTest)
   283  	if err != nil {
   284  		t.Fatalf("The test isEqualTree failed")
   285  	}
   286  	if !isEqual && err != nil {
   287  		t.Fatalf("The copy %q -> %q failed, ", srcTest, dstTest)
   288  	}
   289  }
   290  
   291  // Other test to verify the CopyRecursive
   292  // whose dir$n and dst-dir already exists
   293  // cmd-line equivalent: $ cp -R dir1/ dir2/ dir3/ dst-dir/
   294  //
   295  // dst-dir will content dir{1, 3}
   296  // $ dst-dir/
   297  // ..	dir1/
   298  // ..	dir2/
   299  // ..   dir3/
   300  func TestCpRecursiveMultiple(t *testing.T) {
   301  	recursive = true
   302  	defer resetFlags()
   303  	tempDir, err := ioutil.TempDir("", "TestCpRecursiveMultiple")
   304  	if err != nil {
   305  		t.Fatal(err)
   306  	}
   307  	defer os.RemoveAll(tempDir)
   308  
   309  	dstPrefix := fmt.Sprintf("TestCpDst_%v_container", indentifier)
   310  	dstTest, err := ioutil.TempDir(tempDir, dstPrefix)
   311  	if err != nil {
   312  		t.Fatalf("Failed on build directory %v: %v\n", dstTest, err)
   313  	}
   314  	// create multiple random directories sources
   315  	srcDirs := []string{}
   316  	for i := 0; i < maxDirDepth; i++ {
   317  		srcPrefix := fmt.Sprintf("TestCpSrc_%v_", indentifier)
   318  		srcTest, err := ioutil.TempDir(tempDir, srcPrefix)
   319  		if err != nil {
   320  			t.Fatalf("Failed on build directory %v: %v\n", srcTest, err)
   321  		}
   322  		if err = createFilesTree(srcTest, maxDirDepth, 0); err != nil {
   323  			t.Fatalf("cannot create files tree on directory %v: %v", srcTest, err)
   324  		}
   325  
   326  		srcDirs = append(srcDirs, srcTest)
   327  
   328  	}
   329  	args := srcDirs
   330  	args = append(args, dstTest)
   331  	if err := cp(args); err != nil {
   332  		t.Fatalf("cp %q exit with error: %v", args, err)
   333  	}
   334  	for _, src := range srcDirs {
   335  		_, srcFile := filepath.Split(src)
   336  		dst := filepath.Join(dstTest, srcFile)
   337  		if equal, err := isEqualTree(src, dst); !equal || err != nil {
   338  			t.Fatalf("The copy %q -> %q failed, trees are different", src, dst)
   339  		}
   340  	}
   341  }
   342  
   343  // using -P don't follow symlinks, create other symlink
   344  // cmd-line equivalent: $ cp -P symlink symlink-copy
   345  func TestCpSymlink(t *testing.T) {
   346  	defer resetFlags()
   347  	symlink = true
   348  
   349  	tempDir, err := ioutil.TempDir("", "TestCpSymlink")
   350  	if err != nil {
   351  		t.Fatal(err)
   352  	}
   353  	defer os.RemoveAll(tempDir)
   354  
   355  	srcPrefix := fmt.Sprintf("cpfile_%v_origin", indentifier)
   356  	f, err := randomFile(tempDir, srcPrefix)
   357  	if err != nil {
   358  		t.Fatalf("cannot create a random file: %v", err)
   359  	}
   360  	defer f.Close()
   361  
   362  	srcFpath := f.Name()
   363  	_, srcFname := filepath.Split(srcFpath)
   364  
   365  	linkName := srcFname + "_link"
   366  	// Enter to temp directory to don't have problems
   367  	// with copy links and relative paths
   368  	os.Chdir(tempDir)
   369  	defer os.Chdir("..")
   370  
   371  	if err := os.Symlink(srcFname, linkName); err != nil {
   372  		t.Fatalf("cannot create a link %v -> %v: %v", srcFname, linkName, err)
   373  	}
   374  
   375  	dstFname := fmt.Sprintf("cpfile_%v_dst_link", indentifier)
   376  	if err := copyFile(linkName, dstFname, false); err != nil {
   377  		t.Fatalf("copyFile %q -> %q failed: %v", linkName, dstFname, err)
   378  	}
   379  
   380  	s, err := os.Open(linkName)
   381  	if err != nil {
   382  		t.Fatalf("cannot open the file %v", linkName)
   383  	}
   384  	defer s.Close()
   385  
   386  	d, err := os.Open(dstFname)
   387  	if err != nil {
   388  		t.Fatalf("cannot open the file %v", dstFname)
   389  	}
   390  	defer d.Close()
   391  	dStat, err := d.Stat()
   392  	if err != nil {
   393  		t.Fatalf("cannot stat file %v: %v\n", d, err)
   394  	}
   395  	if L := os.ModeSymlink; dStat.Mode()&L == L {
   396  		t.Fatalf("destination file is not a link %v", d.Name())
   397  	}
   398  
   399  	if equal, err := isEqualFile(s, d); !equal || err != nil {
   400  		t.Fatalf("checksum are different; copies failed %q -> %q: %v", linkName, dstFname, err)
   401  	}
   402  }