github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/fs_test.go (about) 1 package fs 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "os" 9 "strings" 10 "sync" 11 "testing" 12 "time" 13 14 "github.com/spf13/pflag" 15 "github.com/stretchr/testify/require" 16 17 "github.com/rclone/rclone/fs/config/configmap" 18 "github.com/rclone/rclone/fs/fserrors" 19 "github.com/rclone/rclone/lib/pacer" 20 "github.com/stretchr/testify/assert" 21 ) 22 23 func TestFeaturesDisable(t *testing.T) { 24 ft := new(Features) 25 ft.Copy = func(ctx context.Context, src Object, remote string) (Object, error) { 26 return nil, nil 27 } 28 ft.CaseInsensitive = true 29 30 assert.NotNil(t, ft.Copy) 31 assert.Nil(t, ft.Purge) 32 ft.Disable("copy") 33 assert.Nil(t, ft.Copy) 34 assert.Nil(t, ft.Purge) 35 36 assert.True(t, ft.CaseInsensitive) 37 assert.False(t, ft.DuplicateFiles) 38 ft.Disable("caseinsensitive") 39 assert.False(t, ft.CaseInsensitive) 40 assert.False(t, ft.DuplicateFiles) 41 } 42 43 func TestFeaturesList(t *testing.T) { 44 ft := new(Features) 45 names := strings.Join(ft.List(), ",") 46 assert.True(t, strings.Contains(names, ",Copy,")) 47 } 48 49 func TestFeaturesEnabled(t *testing.T) { 50 ft := new(Features) 51 ft.CaseInsensitive = true 52 ft.Purge = func(ctx context.Context, dir string) error { return nil } 53 enabled := ft.Enabled() 54 55 flag, ok := enabled["CaseInsensitive"] 56 assert.Equal(t, true, ok) 57 assert.Equal(t, true, flag, enabled) 58 59 flag, ok = enabled["Purge"] 60 assert.Equal(t, true, ok) 61 assert.Equal(t, true, flag, enabled) 62 63 flag, ok = enabled["DuplicateFiles"] 64 assert.Equal(t, true, ok) 65 assert.Equal(t, false, flag, enabled) 66 67 flag, ok = enabled["Copy"] 68 assert.Equal(t, true, ok) 69 assert.Equal(t, false, flag, enabled) 70 71 assert.Equal(t, len(ft.List()), len(enabled)) 72 } 73 74 func TestFeaturesDisableList(t *testing.T) { 75 ft := new(Features) 76 ft.Copy = func(ctx context.Context, src Object, remote string) (Object, error) { 77 return nil, nil 78 } 79 ft.CaseInsensitive = true 80 81 assert.NotNil(t, ft.Copy) 82 assert.Nil(t, ft.Purge) 83 assert.True(t, ft.CaseInsensitive) 84 assert.False(t, ft.DuplicateFiles) 85 86 ft.DisableList([]string{"copy", "caseinsensitive"}) 87 88 assert.Nil(t, ft.Copy) 89 assert.Nil(t, ft.Purge) 90 assert.False(t, ft.CaseInsensitive) 91 assert.False(t, ft.DuplicateFiles) 92 } 93 94 // Check it satisfies the interface 95 var _ pflag.Value = (*Option)(nil) 96 97 func TestOption(t *testing.T) { 98 d := &Option{ 99 Name: "potato", 100 Value: SizeSuffix(17 << 20), 101 } 102 assert.Equal(t, "17Mi", d.String()) 103 assert.Equal(t, "SizeSuffix", d.Type()) 104 err := d.Set("18M") 105 assert.NoError(t, err) 106 assert.Equal(t, SizeSuffix(18<<20), d.Value) 107 err = d.Set("sdfsdf") 108 assert.Error(t, err) 109 } 110 111 var errFoo = errors.New("foo") 112 113 type dummyPaced struct { 114 retry bool 115 called int 116 wait *sync.Cond 117 } 118 119 func (dp *dummyPaced) fn() (bool, error) { 120 if dp.wait != nil { 121 dp.wait.L.Lock() 122 dp.wait.Wait() 123 dp.wait.L.Unlock() 124 } 125 dp.called++ 126 return dp.retry, errFoo 127 } 128 129 func TestPacerCall(t *testing.T) { 130 ctx := context.Background() 131 config := GetConfig(ctx) 132 expectedCalled := config.LowLevelRetries 133 if expectedCalled == 0 { 134 ctx, config = AddConfig(ctx) 135 expectedCalled = 20 136 config.LowLevelRetries = expectedCalled 137 } 138 p := NewPacer(ctx, pacer.NewDefault(pacer.MinSleep(1*time.Millisecond), pacer.MaxSleep(2*time.Millisecond))) 139 140 dp := &dummyPaced{retry: true} 141 err := p.Call(dp.fn) 142 require.Equal(t, expectedCalled, dp.called) 143 require.Implements(t, (*fserrors.Retrier)(nil), err) 144 } 145 146 func TestPacerCallNoRetry(t *testing.T) { 147 p := NewPacer(context.Background(), pacer.NewDefault(pacer.MinSleep(1*time.Millisecond), pacer.MaxSleep(2*time.Millisecond))) 148 149 dp := &dummyPaced{retry: true} 150 err := p.CallNoRetry(dp.fn) 151 require.Equal(t, 1, dp.called) 152 require.Implements(t, (*fserrors.Retrier)(nil), err) 153 } 154 155 // Test options 156 var ( 157 nouncOption = Option{ 158 Name: "nounc", 159 } 160 copyLinksOption = Option{ 161 Name: "copy_links", 162 Default: false, 163 NoPrefix: true, 164 ShortOpt: "L", 165 Advanced: true, 166 } 167 caseInsensitiveOption = Option{ 168 Name: "case_insensitive", 169 Default: false, 170 Value: true, 171 Advanced: true, 172 } 173 testOptions = Options{nouncOption, copyLinksOption, caseInsensitiveOption} 174 ) 175 176 func TestOptionsSetValues(t *testing.T) { 177 assert.Nil(t, testOptions[0].Default) 178 assert.Equal(t, false, testOptions[1].Default) 179 assert.Equal(t, false, testOptions[2].Default) 180 testOptions.setValues() 181 assert.Equal(t, "", testOptions[0].Default) 182 assert.Equal(t, false, testOptions[1].Default) 183 assert.Equal(t, false, testOptions[2].Default) 184 } 185 186 func TestOptionsGet(t *testing.T) { 187 opt := testOptions.Get("copy_links") 188 assert.Equal(t, ©LinksOption, opt) 189 opt = testOptions.Get("not_found") 190 assert.Nil(t, opt) 191 } 192 193 func TestOptionsOveridden(t *testing.T) { 194 m := configmap.New() 195 m1 := configmap.Simple{ 196 "nounc": "m1", 197 "copy_links": "m1", 198 } 199 m.AddGetter(m1, configmap.PriorityNormal) 200 m2 := configmap.Simple{ 201 "nounc": "m2", 202 "case_insensitive": "m2", 203 } 204 m.AddGetter(m2, configmap.PriorityConfig) 205 m3 := configmap.Simple{ 206 "nounc": "m3", 207 } 208 m.AddGetter(m3, configmap.PriorityDefault) 209 got := testOptions.Overridden(m) 210 assert.Equal(t, configmap.Simple{ 211 "copy_links": "m1", 212 "nounc": "m1", 213 }, got) 214 } 215 216 func TestOptionsNonDefault(t *testing.T) { 217 m := configmap.Simple{} 218 got := testOptions.NonDefault(m) 219 assert.Equal(t, configmap.Simple{}, got) 220 221 m["case_insensitive"] = "false" 222 got = testOptions.NonDefault(m) 223 assert.Equal(t, configmap.Simple{}, got) 224 225 m["case_insensitive"] = "true" 226 got = testOptions.NonDefault(m) 227 assert.Equal(t, configmap.Simple{"case_insensitive": "true"}, got) 228 } 229 230 func TestOptionMarshalJSON(t *testing.T) { 231 out, err := json.MarshalIndent(&caseInsensitiveOption, "", "") 232 assert.NoError(t, err) 233 require.Equal(t, `{ 234 "Name": "case_insensitive", 235 "Help": "", 236 "Provider": "", 237 "Default": false, 238 "Value": true, 239 "ShortOpt": "", 240 "Hide": 0, 241 "Required": false, 242 "IsPassword": false, 243 "NoPrefix": false, 244 "Advanced": true, 245 "Exclusive": false, 246 "Sensitive": false, 247 "DefaultStr": "false", 248 "ValueStr": "true", 249 "Type": "bool" 250 }`, string(out)) 251 } 252 253 func TestOptionGetValue(t *testing.T) { 254 assert.Equal(t, "", nouncOption.GetValue()) 255 assert.Equal(t, false, copyLinksOption.GetValue()) 256 assert.Equal(t, true, caseInsensitiveOption.GetValue()) 257 } 258 259 func TestOptionString(t *testing.T) { 260 assert.Equal(t, "", nouncOption.String()) 261 assert.Equal(t, "false", copyLinksOption.String()) 262 assert.Equal(t, "true", caseInsensitiveOption.String()) 263 } 264 265 func TestOptionSet(t *testing.T) { 266 o := caseInsensitiveOption 267 assert.Equal(t, true, o.Value) 268 err := o.Set("FALSE") 269 assert.NoError(t, err) 270 assert.Equal(t, false, o.Value) 271 272 o = copyLinksOption 273 assert.Equal(t, nil, o.Value) 274 err = o.Set("True") 275 assert.NoError(t, err) 276 assert.Equal(t, true, o.Value) 277 278 err = o.Set("INVALID") 279 assert.Error(t, err) 280 assert.Equal(t, true, o.Value) 281 } 282 283 func TestOptionType(t *testing.T) { 284 assert.Equal(t, "string", nouncOption.Type()) 285 assert.Equal(t, "bool", copyLinksOption.Type()) 286 assert.Equal(t, "bool", caseInsensitiveOption.Type()) 287 } 288 289 func TestOptionFlagName(t *testing.T) { 290 assert.Equal(t, "local-nounc", nouncOption.FlagName("local")) 291 assert.Equal(t, "copy-links", copyLinksOption.FlagName("local")) 292 assert.Equal(t, "local-case-insensitive", caseInsensitiveOption.FlagName("local")) 293 } 294 295 func TestOptionEnvVarName(t *testing.T) { 296 assert.Equal(t, "RCLONE_LOCAL_NOUNC", nouncOption.EnvVarName("local")) 297 assert.Equal(t, "RCLONE_LOCAL_COPY_LINKS", copyLinksOption.EnvVarName("local")) 298 assert.Equal(t, "RCLONE_LOCAL_CASE_INSENSITIVE", caseInsensitiveOption.EnvVarName("local")) 299 } 300 301 func TestOptionGetters(t *testing.T) { 302 // Set up env vars 303 envVars := [][2]string{ 304 {"RCLONE_CONFIG_LOCAL_POTATO_PIE", "yes"}, 305 {"RCLONE_COPY_LINKS", "TRUE"}, 306 {"RCLONE_LOCAL_NOUNC", "NOUNC"}, 307 } 308 for _, ev := range envVars { 309 assert.NoError(t, os.Setenv(ev[0], ev[1])) 310 } 311 defer func() { 312 for _, ev := range envVars { 313 assert.NoError(t, os.Unsetenv(ev[0])) 314 } 315 }() 316 317 fsInfo := &RegInfo{ 318 Name: "local", 319 Prefix: "local", 320 Options: testOptions, 321 } 322 323 oldConfigFileGet := ConfigFileGet 324 ConfigFileGet = func(section, key string) (string, bool) { 325 if section == "sausage" && key == "key1" { 326 return "value1", true 327 } 328 return "", false 329 } 330 defer func() { 331 ConfigFileGet = oldConfigFileGet 332 }() 333 334 // set up getters 335 336 // A configmap.Getter to read from the environment RCLONE_CONFIG_backend_option_name 337 configEnvVarsGetter := configEnvVars("local") 338 339 // A configmap.Getter to read from the environment RCLONE_option_name 340 optionEnvVarsGetter := optionEnvVars{fsInfo} 341 342 // A configmap.Getter to read either the default value or the set 343 // value from the RegInfo.Options 344 regInfoValuesGetterFalse := ®InfoValues{ 345 fsInfo: fsInfo, 346 useDefault: false, 347 } 348 regInfoValuesGetterTrue := ®InfoValues{ 349 fsInfo: fsInfo, 350 useDefault: true, 351 } 352 353 // A configmap.Setter to read from the config file 354 configFileGetter := getConfigFile("sausage") 355 356 for i, test := range []struct { 357 get configmap.Getter 358 key string 359 wantValue string 360 wantOk bool 361 }{ 362 {configEnvVarsGetter, "not_found", "", false}, 363 {configEnvVarsGetter, "potato_pie", "yes", true}, 364 {optionEnvVarsGetter, "not_found", "", false}, 365 {optionEnvVarsGetter, "copy_links", "TRUE", true}, 366 {optionEnvVarsGetter, "nounc", "NOUNC", true}, 367 {optionEnvVarsGetter, "case_insensitive", "", false}, 368 {regInfoValuesGetterFalse, "not_found", "", false}, 369 {regInfoValuesGetterFalse, "case_insensitive", "true", true}, 370 {regInfoValuesGetterFalse, "copy_links", "", false}, 371 {regInfoValuesGetterTrue, "not_found", "", false}, 372 {regInfoValuesGetterTrue, "case_insensitive", "true", true}, 373 {regInfoValuesGetterTrue, "copy_links", "false", true}, 374 {configFileGetter, "not_found", "", false}, 375 {configFileGetter, "key1", "value1", true}, 376 } { 377 what := fmt.Sprintf("%d: %+v: %q", i, test.get, test.key) 378 gotValue, gotOk := test.get.Get(test.key) 379 assert.Equal(t, test.wantValue, gotValue, what) 380 assert.Equal(t, test.wantOk, gotOk, what) 381 } 382 383 }