github.com/flammit/u-root@v0.0.0-20171208172043-8997438e8634/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 }