vfs_case_test.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. package vfs
  2. import (
  3. "context"
  4. "os"
  5. "testing"
  6. "github.com/rclone/rclone/fs"
  7. "github.com/rclone/rclone/fstest"
  8. "github.com/rclone/rclone/vfs/vfscommon"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/require"
  11. "golang.org/x/text/unicode/norm"
  12. )
  13. func TestCaseSensitivity(t *testing.T) {
  14. r := fstest.NewRun(t)
  15. if r.Fremote.Features().CaseInsensitive {
  16. t.Skip("Can't test case sensitivity - this remote is officially not case-sensitive")
  17. }
  18. // Create test files
  19. ctx := context.Background()
  20. file1 := r.WriteObject(ctx, "FiLeA", "data1", t1)
  21. file2 := r.WriteObject(ctx, "FiLeB", "data2", t2)
  22. r.CheckRemoteItems(t, file1, file2)
  23. // Create file3 with name differing from file2 name only by case.
  24. // On a case-Sensitive remote this will be a separate file.
  25. // On a case-INsensitive remote this file will either not exist
  26. // or overwrite file2 depending on how file system diverges.
  27. // On a box.com remote this step will even fail.
  28. file3 := r.WriteObject(ctx, "FilEb", "data3", t3)
  29. // Create a case-Sensitive and case-INsensitive VFS
  30. optCS := vfscommon.DefaultOpt
  31. optCS.CaseInsensitive = false
  32. vfsCS := New(r.Fremote, &optCS)
  33. defer cleanupVFS(t, vfsCS)
  34. optCI := vfscommon.DefaultOpt
  35. optCI.CaseInsensitive = true
  36. vfsCI := New(r.Fremote, &optCI)
  37. defer cleanupVFS(t, vfsCI)
  38. // Run basic checks that must pass on VFS of any type.
  39. assertFileDataVFS(t, vfsCI, "FiLeA", "data1")
  40. assertFileDataVFS(t, vfsCS, "FiLeA", "data1")
  41. // Detect case sensitivity of the underlying remote.
  42. remoteIsOK := true
  43. if !checkFileDataVFS(t, vfsCS, "FiLeA", "data1") {
  44. remoteIsOK = false
  45. }
  46. if !checkFileDataVFS(t, vfsCS, "FiLeB", "data2") {
  47. remoteIsOK = false
  48. }
  49. if !checkFileDataVFS(t, vfsCS, "FilEb", "data3") {
  50. remoteIsOK = false
  51. }
  52. // The remaining test is only meaningful on a case-Sensitive file system.
  53. if !remoteIsOK {
  54. t.Skip("Can't test case sensitivity - this remote doesn't comply as case-sensitive")
  55. }
  56. // Continue with test as the underlying remote is fully case-Sensitive.
  57. r.CheckRemoteItems(t, file1, file2, file3)
  58. // See how VFS handles case-INsensitive flag
  59. assertFileDataVFS(t, vfsCI, "FiLeA", "data1")
  60. assertFileDataVFS(t, vfsCI, "fileA", "data1")
  61. assertFileDataVFS(t, vfsCI, "filea", "data1")
  62. assertFileDataVFS(t, vfsCI, "FILEA", "data1")
  63. assertFileDataVFS(t, vfsCI, "FiLeB", "data2")
  64. assertFileDataVFS(t, vfsCI, "FilEb", "data3")
  65. fd, err := vfsCI.OpenFile("fileb", os.O_RDONLY, 0777)
  66. assert.Nil(t, fd)
  67. assert.Error(t, err)
  68. assert.NotEqual(t, err, ENOENT)
  69. fd, err = vfsCI.OpenFile("FILEB", os.O_RDONLY, 0777)
  70. assert.Nil(t, fd)
  71. assert.Error(t, err)
  72. assert.NotEqual(t, err, ENOENT)
  73. // Run the same set of checks with case-Sensitive VFS, for comparison.
  74. assertFileDataVFS(t, vfsCS, "FiLeA", "data1")
  75. assertFileAbsentVFS(t, vfsCS, "fileA")
  76. assertFileAbsentVFS(t, vfsCS, "filea")
  77. assertFileAbsentVFS(t, vfsCS, "FILEA")
  78. assertFileDataVFS(t, vfsCS, "FiLeB", "data2")
  79. assertFileDataVFS(t, vfsCS, "FilEb", "data3")
  80. assertFileAbsentVFS(t, vfsCS, "fileb")
  81. assertFileAbsentVFS(t, vfsCS, "FILEB")
  82. }
  83. func checkFileDataVFS(t *testing.T, vfs *VFS, name string, expect string) bool {
  84. fd, err := vfs.OpenFile(name, os.O_RDONLY, 0777)
  85. if fd == nil || err != nil {
  86. return false
  87. }
  88. defer func() {
  89. // File must be closed - otherwise Run.cleanUp() will fail on Windows.
  90. _ = fd.Close()
  91. }()
  92. fh, ok := fd.(*ReadFileHandle)
  93. if !ok {
  94. return false
  95. }
  96. size := len(expect)
  97. buf := make([]byte, size)
  98. num, err := fh.Read(buf)
  99. if err != nil || num != size {
  100. return false
  101. }
  102. return string(buf) == expect
  103. }
  104. func assertFileDataVFS(t *testing.T, vfs *VFS, name string, expect string) {
  105. fd, errOpen := vfs.OpenFile(name, os.O_RDONLY, 0777)
  106. assert.NotNil(t, fd)
  107. assert.NoError(t, errOpen)
  108. defer func() {
  109. // File must be closed - otherwise Run.cleanUp() will fail on Windows.
  110. if errOpen == nil && fd != nil {
  111. _ = fd.Close()
  112. }
  113. }()
  114. fh, ok := fd.(*ReadFileHandle)
  115. require.True(t, ok)
  116. size := len(expect)
  117. buf := make([]byte, size)
  118. numRead, errRead := fh.Read(buf)
  119. assert.NoError(t, errRead)
  120. assert.Equal(t, numRead, size)
  121. assert.Equal(t, string(buf), expect)
  122. }
  123. func assertFileAbsentVFS(t *testing.T, vfs *VFS, name string) {
  124. fd, err := vfs.OpenFile(name, os.O_RDONLY, 0777)
  125. defer func() {
  126. // File must be closed - otherwise Run.cleanUp() will fail on Windows.
  127. if err == nil && fd != nil {
  128. _ = fd.Close()
  129. }
  130. }()
  131. assert.Nil(t, fd)
  132. assert.Error(t, err)
  133. assert.Equal(t, err, ENOENT)
  134. }
  135. func TestUnicodeNormalization(t *testing.T) {
  136. r := fstest.NewRun(t)
  137. var (
  138. nfc = norm.NFC.String(norm.NFD.String("測試_Русский___ě_áñ"))
  139. nfd = norm.NFD.String(nfc)
  140. both = "normal name with no special characters.txt"
  141. )
  142. // Create test files
  143. ctx := context.Background()
  144. file1 := r.WriteObject(ctx, both, "data1", t1)
  145. file2 := r.WriteObject(ctx, nfc, "data2", t2)
  146. r.CheckRemoteItems(t, file1, file2)
  147. // Create VFS
  148. opt := vfscommon.DefaultOpt
  149. vfs := New(r.Fremote, &opt)
  150. defer cleanupVFS(t, vfs)
  151. // assert that both files are found under NFD-normalized names
  152. assertFileDataVFS(t, vfs, norm.NFD.String(both), "data1")
  153. assertFileDataVFS(t, vfs, nfd, "data2")
  154. // change ci.NoUnicodeNormalization to true and verify that only file1 is found
  155. ci := fs.GetConfig(ctx) // need to set the global config here as the *Dir methods don't take a ctx param
  156. oldVal := ci.NoUnicodeNormalization
  157. defer func() { fs.GetConfig(ctx).NoUnicodeNormalization = oldVal }() // restore the prior value after the test
  158. ci.NoUnicodeNormalization = true
  159. assertFileDataVFS(t, vfs, norm.NFD.String(both), "data1")
  160. assertFileAbsentVFS(t, vfs, nfd)
  161. }