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