union_internal_test.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package union
  2. import (
  3. "bytes"
  4. "context"
  5. "fmt"
  6. "testing"
  7. "time"
  8. "github.com/rclone/rclone/fs"
  9. "github.com/rclone/rclone/fs/object"
  10. "github.com/rclone/rclone/fs/operations"
  11. "github.com/rclone/rclone/fstest"
  12. "github.com/rclone/rclone/fstest/fstests"
  13. "github.com/rclone/rclone/lib/random"
  14. "github.com/stretchr/testify/assert"
  15. "github.com/stretchr/testify/require"
  16. )
  17. // MakeTestDirs makes directories in /tmp for testing
  18. func MakeTestDirs(t *testing.T, n int) (dirs []string) {
  19. for i := 1; i <= n; i++ {
  20. dir := t.TempDir()
  21. dirs = append(dirs, dir)
  22. }
  23. return dirs
  24. }
  25. func (f *Fs) TestInternalReadOnly(t *testing.T) {
  26. if f.name != "TestUnionRO" {
  27. t.Skip("Only on RO union")
  28. }
  29. dir := "TestInternalReadOnly"
  30. ctx := context.Background()
  31. rofs := f.upstreams[len(f.upstreams)-1]
  32. assert.False(t, rofs.IsWritable())
  33. // Put a file onto the read only fs
  34. contents := random.String(50)
  35. file1 := fstest.NewItem(dir+"/file.txt", contents, time.Now())
  36. obj1 := fstests.PutTestContents(ctx, t, rofs, &file1, contents, true)
  37. // Check read from readonly fs via union
  38. o, err := f.NewObject(ctx, file1.Path)
  39. require.NoError(t, err)
  40. assert.Equal(t, int64(50), o.Size())
  41. // Now call Update on the union Object with new data
  42. contents2 := random.String(100)
  43. file2 := fstest.NewItem(dir+"/file.txt", contents2, time.Now())
  44. in := bytes.NewBufferString(contents2)
  45. src := object.NewStaticObjectInfo(file2.Path, file2.ModTime, file2.Size, true, nil, nil)
  46. err = o.Update(ctx, in, src)
  47. require.NoError(t, err)
  48. assert.Equal(t, int64(100), o.Size())
  49. // Check we read the new object via the union
  50. o, err = f.NewObject(ctx, file1.Path)
  51. require.NoError(t, err)
  52. assert.Equal(t, int64(100), o.Size())
  53. // Remove the object
  54. assert.NoError(t, o.Remove(ctx))
  55. // Check we read the old object in the read only layer now
  56. o, err = f.NewObject(ctx, file1.Path)
  57. require.NoError(t, err)
  58. assert.Equal(t, int64(50), o.Size())
  59. // Remove file and dir from read only fs
  60. assert.NoError(t, obj1.Remove(ctx))
  61. assert.NoError(t, rofs.Rmdir(ctx, dir))
  62. }
  63. func (f *Fs) InternalTest(t *testing.T) {
  64. t.Run("ReadOnly", f.TestInternalReadOnly)
  65. }
  66. var _ fstests.InternalTester = (*Fs)(nil)
  67. // This specifically tests a union of local which can Move but not
  68. // Copy and :memory: which can Copy but not Move to makes sure that
  69. // the resulting union can Move
  70. func TestMoveCopy(t *testing.T) {
  71. if *fstest.RemoteName != "" {
  72. t.Skip("Skipping as -remote set")
  73. }
  74. ctx := context.Background()
  75. dirs := MakeTestDirs(t, 1)
  76. fsString := fmt.Sprintf(":union,upstreams='%s :memory:bucket':", dirs[0])
  77. f, err := fs.NewFs(ctx, fsString)
  78. require.NoError(t, err)
  79. unionFs := f.(*Fs)
  80. fLocal := unionFs.upstreams[0].Fs
  81. fMemory := unionFs.upstreams[1].Fs
  82. t.Run("Features", func(t *testing.T) {
  83. assert.NotNil(t, f.Features().Move)
  84. assert.Nil(t, f.Features().Copy)
  85. // Check underlying are as we are expect
  86. assert.NotNil(t, fLocal.Features().Move)
  87. assert.Nil(t, fLocal.Features().Copy)
  88. assert.Nil(t, fMemory.Features().Move)
  89. assert.NotNil(t, fMemory.Features().Copy)
  90. })
  91. // Put a file onto the local fs
  92. contentsLocal := random.String(50)
  93. fileLocal := fstest.NewItem("local.txt", contentsLocal, time.Now())
  94. _ = fstests.PutTestContents(ctx, t, fLocal, &fileLocal, contentsLocal, true)
  95. objLocal, err := f.NewObject(ctx, fileLocal.Path)
  96. require.NoError(t, err)
  97. // Put a file onto the memory fs
  98. contentsMemory := random.String(60)
  99. fileMemory := fstest.NewItem("memory.txt", contentsMemory, time.Now())
  100. _ = fstests.PutTestContents(ctx, t, fMemory, &fileMemory, contentsMemory, true)
  101. objMemory, err := f.NewObject(ctx, fileMemory.Path)
  102. require.NoError(t, err)
  103. fstest.CheckListing(t, f, []fstest.Item{fileLocal, fileMemory})
  104. t.Run("MoveLocal", func(t *testing.T) {
  105. fileLocal.Path = "local-renamed.txt"
  106. _, err := operations.Move(ctx, f, nil, fileLocal.Path, objLocal)
  107. require.NoError(t, err)
  108. fstest.CheckListing(t, f, []fstest.Item{fileLocal, fileMemory})
  109. // Check can retrieve object from union
  110. obj, err := f.NewObject(ctx, fileLocal.Path)
  111. require.NoError(t, err)
  112. assert.Equal(t, fileLocal.Size, obj.Size())
  113. // Check can retrieve object from underlying
  114. obj, err = fLocal.NewObject(ctx, fileLocal.Path)
  115. require.NoError(t, err)
  116. assert.Equal(t, fileLocal.Size, obj.Size())
  117. t.Run("MoveMemory", func(t *testing.T) {
  118. fileMemory.Path = "memory-renamed.txt"
  119. _, err := operations.Move(ctx, f, nil, fileMemory.Path, objMemory)
  120. require.NoError(t, err)
  121. fstest.CheckListing(t, f, []fstest.Item{fileLocal, fileMemory})
  122. // Check can retrieve object from union
  123. obj, err := f.NewObject(ctx, fileMemory.Path)
  124. require.NoError(t, err)
  125. assert.Equal(t, fileMemory.Size, obj.Size())
  126. // Check can retrieve object from underlying
  127. obj, err = fMemory.NewObject(ctx, fileMemory.Path)
  128. require.NoError(t, err)
  129. assert.Equal(t, fileMemory.Size, obj.Size())
  130. })
  131. })
  132. }