gopkg.in/hugelgupf/u-root.v9@v9.0.0-20180831063832-3f6f1057f09b/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 }