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, &copyLinksOption, 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 := &regInfoValues{
   345  		fsInfo:     fsInfo,
   346  		useDefault: false,
   347  	}
   348  	regInfoValuesGetterTrue := &regInfoValues{
   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  }