github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/vfs/vfs_test.go (about) 1 // Test suite for vfs 2 3 package vfs 4 5 import ( 6 "context" 7 "errors" 8 "fmt" 9 "io" 10 "os" 11 "testing" 12 "time" 13 14 _ "github.com/rclone/rclone/backend/all" // import all the backends 15 "github.com/rclone/rclone/fs" 16 "github.com/rclone/rclone/fstest" 17 "github.com/rclone/rclone/vfs/vfscommon" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 ) 21 22 // Some times used in the tests 23 var ( 24 t1 = fstest.Time("2001-02-03T04:05:06.499999999Z") 25 t2 = fstest.Time("2011-12-25T12:59:59.123456789Z") 26 t3 = fstest.Time("2011-12-30T12:59:59.000000000Z") 27 ) 28 29 // Constants uses in the tests 30 const ( 31 writeBackDelay = 100 * time.Millisecond // A short writeback delay for testing 32 waitForWritersDelay = 30 * time.Second // time to wait for existing writers 33 ) 34 35 // TestMain drives the tests 36 func TestMain(m *testing.M) { 37 fstest.TestMain(m) 38 } 39 40 // Clean up a test VFS 41 func cleanupVFS(t *testing.T, vfs *VFS) { 42 vfs.WaitForWriters(waitForWritersDelay) 43 err := vfs.CleanUp() 44 require.NoError(t, err) 45 vfs.Shutdown() 46 } 47 48 // Create a new VFS 49 func newTestVFSOpt(t *testing.T, opt *vfscommon.Options) (r *fstest.Run, vfs *VFS) { 50 r = fstest.NewRun(t) 51 vfs = New(r.Fremote, opt) 52 t.Cleanup(func() { 53 cleanupVFS(t, vfs) 54 }) 55 return r, vfs 56 } 57 58 // Create a new VFS with default options 59 func newTestVFS(t *testing.T) (r *fstest.Run, vfs *VFS) { 60 return newTestVFSOpt(t, nil) 61 } 62 63 // Check baseHandle performs as advertised 64 func TestVFSbaseHandle(t *testing.T) { 65 fh := baseHandle{} 66 67 err := fh.Chdir() 68 assert.Equal(t, ENOSYS, err) 69 70 err = fh.Chmod(0) 71 assert.Equal(t, ENOSYS, err) 72 73 err = fh.Chown(0, 0) 74 assert.Equal(t, ENOSYS, err) 75 76 err = fh.Close() 77 assert.Equal(t, ENOSYS, err) 78 79 fd := fh.Fd() 80 assert.Equal(t, uintptr(0), fd) 81 82 name := fh.Name() 83 assert.Equal(t, "", name) 84 85 _, err = fh.Read(nil) 86 assert.Equal(t, ENOSYS, err) 87 88 _, err = fh.ReadAt(nil, 0) 89 assert.Equal(t, ENOSYS, err) 90 91 _, err = fh.Readdir(0) 92 assert.Equal(t, ENOSYS, err) 93 94 _, err = fh.Readdirnames(0) 95 assert.Equal(t, ENOSYS, err) 96 97 _, err = fh.Seek(0, io.SeekStart) 98 assert.Equal(t, ENOSYS, err) 99 100 _, err = fh.Stat() 101 assert.Equal(t, ENOSYS, err) 102 103 err = fh.Sync() 104 assert.Equal(t, nil, err) 105 106 err = fh.Truncate(0) 107 assert.Equal(t, ENOSYS, err) 108 109 _, err = fh.Write(nil) 110 assert.Equal(t, ENOSYS, err) 111 112 _, err = fh.WriteAt(nil, 0) 113 assert.Equal(t, ENOSYS, err) 114 115 _, err = fh.WriteString("") 116 assert.Equal(t, ENOSYS, err) 117 118 err = fh.Flush() 119 assert.Equal(t, ENOSYS, err) 120 121 err = fh.Release() 122 assert.Equal(t, ENOSYS, err) 123 124 node := fh.Node() 125 assert.Nil(t, node) 126 } 127 128 // TestVFSNew sees if the New command works properly 129 func TestVFSNew(t *testing.T) { 130 // Check active cache has this many entries 131 checkActiveCacheEntries := func(i int) { 132 _, count := activeCacheEntries() 133 assert.Equal(t, i, count) 134 } 135 136 checkActiveCacheEntries(0) 137 138 r, vfs := newTestVFS(t) 139 140 // Check making a VFS with nil options 141 var defaultOpt = vfscommon.DefaultOpt 142 defaultOpt.DirPerms |= os.ModeDir 143 assert.Equal(t, vfs.Opt, defaultOpt) 144 assert.Equal(t, vfs.f, r.Fremote) 145 146 checkActiveCacheEntries(1) 147 148 // Check that we get the same VFS if we ask for it again with 149 // the same options 150 vfs2 := New(r.Fremote, nil) 151 assert.Equal(t, fmt.Sprintf("%p", vfs), fmt.Sprintf("%p", vfs2)) 152 153 checkActiveCacheEntries(1) 154 155 // Shut the new VFS down and check the cache still has stuff in 156 vfs2.Shutdown() 157 158 checkActiveCacheEntries(1) 159 160 cleanupVFS(t, vfs) 161 162 checkActiveCacheEntries(0) 163 } 164 165 // TestVFSNewWithOpts sees if the New command works properly 166 func TestVFSNewWithOpts(t *testing.T) { 167 var opt = vfscommon.DefaultOpt 168 opt.DirPerms = 0777 169 opt.FilePerms = 0666 170 opt.Umask = 0002 171 _, vfs := newTestVFSOpt(t, &opt) 172 173 assert.Equal(t, os.FileMode(0775)|os.ModeDir, vfs.Opt.DirPerms) 174 assert.Equal(t, os.FileMode(0664), vfs.Opt.FilePerms) 175 } 176 177 // TestVFSRoot checks root directory is present and correct 178 func TestVFSRoot(t *testing.T) { 179 _, vfs := newTestVFS(t) 180 181 root, err := vfs.Root() 182 require.NoError(t, err) 183 assert.Equal(t, vfs.root, root) 184 assert.True(t, root.IsDir()) 185 assert.Equal(t, vfs.Opt.DirPerms.Perm(), root.Mode().Perm()) 186 } 187 188 func TestVFSStat(t *testing.T) { 189 r, vfs := newTestVFS(t) 190 191 file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1) 192 file2 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2) 193 r.CheckRemoteItems(t, file1, file2) 194 195 node, err := vfs.Stat("file1") 196 require.NoError(t, err) 197 assert.True(t, node.IsFile()) 198 assert.Equal(t, "file1", node.Name()) 199 200 node, err = vfs.Stat("dir") 201 require.NoError(t, err) 202 assert.True(t, node.IsDir()) 203 assert.Equal(t, "dir", node.Name()) 204 205 node, err = vfs.Stat("dir/file2") 206 require.NoError(t, err) 207 assert.True(t, node.IsFile()) 208 assert.Equal(t, "file2", node.Name()) 209 210 _, err = vfs.Stat("not found") 211 assert.Equal(t, os.ErrNotExist, err) 212 213 _, err = vfs.Stat("dir/not found") 214 assert.Equal(t, os.ErrNotExist, err) 215 216 _, err = vfs.Stat("not found/not found") 217 assert.Equal(t, os.ErrNotExist, err) 218 219 _, err = vfs.Stat("file1/under a file") 220 assert.Equal(t, os.ErrNotExist, err) 221 } 222 223 func TestVFSStatParent(t *testing.T) { 224 r, vfs := newTestVFS(t) 225 226 file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1) 227 file2 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2) 228 r.CheckRemoteItems(t, file1, file2) 229 230 node, leaf, err := vfs.StatParent("file1") 231 require.NoError(t, err) 232 assert.True(t, node.IsDir()) 233 assert.Equal(t, "/", node.Name()) 234 assert.Equal(t, "file1", leaf) 235 236 node, leaf, err = vfs.StatParent("dir/file2") 237 require.NoError(t, err) 238 assert.True(t, node.IsDir()) 239 assert.Equal(t, "dir", node.Name()) 240 assert.Equal(t, "file2", leaf) 241 242 node, leaf, err = vfs.StatParent("not found") 243 require.NoError(t, err) 244 assert.True(t, node.IsDir()) 245 assert.Equal(t, "/", node.Name()) 246 assert.Equal(t, "not found", leaf) 247 248 _, _, err = vfs.StatParent("not found dir/not found") 249 assert.Equal(t, os.ErrNotExist, err) 250 251 _, _, err = vfs.StatParent("file1/under a file") 252 assert.Equal(t, os.ErrExist, err) 253 } 254 255 func TestVFSOpenFile(t *testing.T) { 256 r, vfs := newTestVFS(t) 257 258 file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1) 259 file2 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2) 260 r.CheckRemoteItems(t, file1, file2) 261 262 fd, err := vfs.OpenFile("file1", os.O_RDONLY, 0777) 263 require.NoError(t, err) 264 assert.NotNil(t, fd) 265 require.NoError(t, fd.Close()) 266 267 fd, err = vfs.OpenFile("dir", os.O_RDONLY, 0777) 268 require.NoError(t, err) 269 assert.NotNil(t, fd) 270 require.NoError(t, fd.Close()) 271 272 fd, err = vfs.OpenFile("dir/new_file.txt", os.O_RDONLY, 0777) 273 assert.Equal(t, os.ErrNotExist, err) 274 assert.Nil(t, fd) 275 276 fd, err = vfs.OpenFile("dir/new_file.txt", os.O_WRONLY|os.O_CREATE, 0777) 277 require.NoError(t, err) 278 assert.NotNil(t, fd) 279 err = fd.Close() 280 if !errors.Is(err, fs.ErrorCantUploadEmptyFiles) { 281 require.NoError(t, err) 282 } 283 284 fd, err = vfs.OpenFile("not found/new_file.txt", os.O_WRONLY|os.O_CREATE, 0777) 285 assert.Equal(t, os.ErrNotExist, err) 286 assert.Nil(t, fd) 287 } 288 289 func TestVFSRename(t *testing.T) { 290 r, vfs := newTestVFS(t) 291 292 features := r.Fremote.Features() 293 if features.Move == nil && features.Copy == nil { 294 t.Skip("skip as can't rename files") 295 } 296 297 file1 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2) 298 r.CheckRemoteItems(t, file1) 299 300 err := vfs.Rename("dir/file2", "dir/file1") 301 require.NoError(t, err) 302 file1.Path = "dir/file1" 303 r.CheckRemoteItems(t, file1) 304 305 err = vfs.Rename("dir/file1", "file0") 306 require.NoError(t, err) 307 file1.Path = "file0" 308 r.CheckRemoteItems(t, file1) 309 310 err = vfs.Rename("not found/file0", "file0") 311 assert.Equal(t, os.ErrNotExist, err) 312 313 err = vfs.Rename("file0", "not found/file0") 314 assert.Equal(t, os.ErrNotExist, err) 315 } 316 317 func TestVFSStatfs(t *testing.T) { 318 r, vfs := newTestVFS(t) 319 320 // pre-conditions 321 assert.Nil(t, vfs.usage) 322 assert.True(t, vfs.usageTime.IsZero()) 323 324 aboutSupported := r.Fremote.Features().About != nil 325 326 // read 327 total, used, free := vfs.Statfs() 328 if !aboutSupported { 329 assert.Equal(t, int64(unknownFreeBytes), total) 330 assert.Equal(t, int64(unknownFreeBytes), free) 331 assert.Equal(t, int64(0), used) 332 return // can't test anything else if About not supported 333 } 334 require.NotNil(t, vfs.usage) 335 assert.False(t, vfs.usageTime.IsZero()) 336 if vfs.usage.Total != nil { 337 assert.Equal(t, *vfs.usage.Total, total) 338 } else { 339 assert.True(t, total >= int64(unknownFreeBytes)) 340 } 341 if vfs.usage.Free != nil { 342 assert.Equal(t, *vfs.usage.Free, free) 343 } else { 344 if vfs.usage.Total != nil && vfs.usage.Used != nil { 345 assert.Equal(t, free, total-used) 346 } else { 347 assert.True(t, free >= int64(unknownFreeBytes)) 348 } 349 } 350 if vfs.usage.Used != nil { 351 assert.Equal(t, *vfs.usage.Used, used) 352 } else { 353 assert.Equal(t, int64(0), used) 354 } 355 356 // read cached 357 oldUsage := vfs.usage 358 oldTime := vfs.usageTime 359 total2, used2, free2 := vfs.Statfs() 360 assert.Equal(t, oldUsage, vfs.usage) 361 assert.Equal(t, total, total2) 362 assert.Equal(t, used, used2) 363 assert.Equal(t, free, free2) 364 assert.Equal(t, oldTime, vfs.usageTime) 365 } 366 367 func TestVFSMkdir(t *testing.T) { 368 r, vfs := newTestVFS(t) 369 370 if !r.Fremote.Features().CanHaveEmptyDirectories { 371 return // can't test if can't have empty directories 372 } 373 374 r.CheckRemoteListing(t, nil, []string{}) 375 376 // Try making the root 377 err := vfs.Mkdir("", 0777) 378 require.NoError(t, err) 379 r.CheckRemoteListing(t, nil, []string{}) 380 381 // Try making a sub directory 382 err = vfs.Mkdir("a", 0777) 383 require.NoError(t, err) 384 385 r.CheckRemoteListing(t, nil, []string{"a"}) 386 387 // Try making an existing directory 388 err = vfs.Mkdir("a", 0777) 389 require.NoError(t, err) 390 391 r.CheckRemoteListing(t, nil, []string{"a"}) 392 393 // Try making a new directory 394 err = vfs.Mkdir("b/", 0777) 395 require.NoError(t, err) 396 397 r.CheckRemoteListing(t, nil, []string{"a", "b"}) 398 399 // Try making a new directory 400 err = vfs.Mkdir("/c", 0777) 401 require.NoError(t, err) 402 403 r.CheckRemoteListing(t, nil, []string{"a", "b", "c"}) 404 405 // Try making a new directory 406 err = vfs.Mkdir("/d/", 0777) 407 require.NoError(t, err) 408 409 r.CheckRemoteListing(t, nil, []string{"a", "b", "c", "d"}) 410 } 411 412 func TestVFSMkdirAll(t *testing.T) { 413 r, vfs := newTestVFS(t) 414 415 if !r.Fremote.Features().CanHaveEmptyDirectories { 416 return // can't test if can't have empty directories 417 } 418 419 r.CheckRemoteListing(t, nil, []string{}) 420 421 // Try making the root 422 err := vfs.MkdirAll("", 0777) 423 require.NoError(t, err) 424 r.CheckRemoteListing(t, nil, []string{}) 425 426 // Try making a sub directory 427 err = vfs.MkdirAll("a/b/c/d", 0777) 428 require.NoError(t, err) 429 430 r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"}) 431 432 // Try making an existing directory 433 err = vfs.MkdirAll("a/b/c", 0777) 434 require.NoError(t, err) 435 436 r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"}) 437 438 // Try making an existing directory 439 err = vfs.MkdirAll("/a/b/c/", 0777) 440 require.NoError(t, err) 441 442 r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"}) 443 } 444 445 func TestFillInMissingSizes(t *testing.T) { 446 const unknownFree = 10 447 for _, test := range []struct { 448 total, free, used int64 449 wantTotal, wantUsed, wantFree int64 450 }{ 451 { 452 total: 20, free: 5, used: 15, 453 wantTotal: 20, wantFree: 5, wantUsed: 15, 454 }, 455 { 456 total: 20, free: 5, used: -1, 457 wantTotal: 20, wantFree: 5, wantUsed: 15, 458 }, 459 { 460 total: 20, free: -1, used: 15, 461 wantTotal: 20, wantFree: 5, wantUsed: 15, 462 }, 463 { 464 total: 20, free: -1, used: -1, 465 wantTotal: 20, wantFree: 20, wantUsed: 0, 466 }, 467 { 468 total: -1, free: 5, used: 15, 469 wantTotal: 20, wantFree: 5, wantUsed: 15, 470 }, 471 { 472 total: -1, free: 15, used: -1, 473 wantTotal: 15, wantFree: 15, wantUsed: 0, 474 }, 475 { 476 total: -1, free: -1, used: 15, 477 wantTotal: 25, wantFree: 10, wantUsed: 15, 478 }, 479 { 480 total: -1, free: -1, used: -1, 481 wantTotal: 10, wantFree: 10, wantUsed: 0, 482 }, 483 } { 484 t.Run(fmt.Sprintf("total=%d,free=%d,used=%d", test.total, test.free, test.used), func(t *testing.T) { 485 gotTotal, gotUsed, gotFree := fillInMissingSizes(test.total, test.used, test.free, unknownFree) 486 assert.Equal(t, test.wantTotal, gotTotal, "total") 487 assert.Equal(t, test.wantUsed, gotUsed, "used") 488 assert.Equal(t, test.wantFree, gotFree, "free") 489 }) 490 } 491 }